티스토리 뷰

도로 그리기?

polygon이랑 line 받아오니까 쉬울줄 알았음.

과소평가한 대가로 2주라는 시간이 걸림.

 

생각보다 매우 쉽지만 은근 노가다가 필요함.

 

지난번 서버 붙인 것에 이어서 함.

1. 지도 넣어줄 div 추가

id는 대부분 map을 사용하니 나도 따라서 id가 map인 div를 넣어줌.

귀찮아서 코드로 추가함.

let mapElement = null;
if(document.getElementById('map')){
    mapElement = document.getElementById('map')
}
else {
    mapElement = document.createElement("div");
    mapElement.id = "map";
    mapElement.style.width = '100%';
    mapElement.style.height = "100%";
    document.body.appendChild(mapElement);
}

 

2. map에 ol의 Map 삽입

import {Map, View} from 'ol';
... 생략 ...

const map = new Map({
    target: mapElement,
    layers: 그려야 할 레이어,
    view: new View({
        center: fromLonLat([127.024612, 37.532600]),
        zoom: 17,
        maxZoom: 20,
        minZoom: 1,
    })
});

위에 만든 mapElement에 넣어주기 때문에 target은 mapElement임.

layers는 벡터레이어나 이미지타일 등을 넣게 됨.

나는 pg_tileserv를 통해 벡터 타일 데이터를 받아와서 할거임.

view는 화면에 보여질 상태를 지정하는데 center 좌표값과 zoom, 최대 zoom, 최소 zoom을 지정해줌.

 

3. VectorTileLayer 추가

import VectorTileLayer from 'ol/layer/VectorTile';
... 생략 ...

const planet_osm_line_casing = new VectorTileLayer({
    source: new VectorTileSource({
        url: `http://localhost:7800/public.planet_osm_line/{z}/{x}/{y}.pbf`,
        format: new MVT(),
        overlaps: false
    }),
    style: (feature) => {
        let highwayName = feature.get('highway');
        let line_casing_stroke = null;
        
        if(highwayName){
        	let width = null;
            
            if(highwayName === 'trunk_link' || highwayName === 'trunk') {
                width = 5.5;
            }
            else if(highwayName === 'secondary' || highwayName === 'primary' || highwayName === 'motorway' || highwayName === 'motorway_link') {
                width = 6;
            }
            else if (highwayName === 'tertiary' || highwayName === 'residential') {
                width = 5;
            }
            else if (highwayName === 'service') {
                width = 3;
            }
            
            line_casing_stroke = width ? new Style({
                stroke: new Stroke({
                    color: 'gray',
                    width: width,
                    lineCap : 'round'
                }),
            }) || null;
        }
        
        return line_casing_stroke;
    }
});

const planet_osm_line_fill = new VectorTileLayer({
    source: new VectorTileSource({
        url: `http://localhost:7800/public.planet_osm_line/{z}/{x}/{y}.pbf`,
        format: new MVT(),
        overlaps: false
    }),
    style: (feature) => {
        let highwayName = feature.get('highway');
        let line_fill_stroke = null;
        
        if(highwayName){
        	let color = null;
            let width = null;
            
            if(highwayName === 'trunk_link' || highwayName === 'trunk') {
                width = 4.5;
            }
            else if(highwayName === 'secondary' || highwayName === 'primary' || highwayName === 'motorway' || highwayName === 'motorway_link') {
                width = 5;
            }
            else if (highwayName === 'tertiary' || highwayName === 'residential') {
                width = 4;
            }
            else if (highwayName === 'service') {
                width = 2;
            }

            if(highwayName === 'trunk_link' || highwayName === 'trunk' || highwayName === 'secondary' || highwayName === 'primary' || highwayName === 'motorway' || highwayName === 'motorway_link') {
                color = '#fea'
            }
            else if (highwayName === 'tertiary' || highwayName === 'residential' || highwayName === 'service') {
                color = '#f0f0f0'
            }
            
            line_fill_stroke = width ? new Style({
                stroke: new Stroke({
                    color: color,
                    width: width,
                    lineCap : 'round'
                }),
            }) || null;
        }
        
        return line_fill_stroke;
    }
});

 

planet_osm_line_casing은 테두리처럼 되도록 보이기 위해 추가함.

planet_osm_line_fill은 도로로 칠해질 부분이라 보면 됨.

사람은 둥굴게 사는게 좋기 때문에 lineCap은 round로 표시해줌.

 

4. map 수정

import {Map, View} from 'ol';
... 생략 ...

const map = new Map({
    target: mapElement,
    layers: [planet_osm_line_casing, planet_osm_line_fill],
    view: new View({
        center: fromLonLat([127.024612, 37.532600]),
        zoom: 17,
        maxZoom: 20,
        minZoom: 1,
    })
});

layers의 배열에 index가 작을 수록 먼저 그려짐.

fill이 casing 위에 올라와야 하기 때문에 fill이 뒤에 그려지도록 순서 배치함.

 

이렇게 하고 실행해보면 아래 이미지처럼 나옴.

그려진 지도 중 일부 영역 캡처

나는 코드 정리하면서 이것저것 추가한게 많음.

근데 layers 순서를 줘도 확대/축소 하다보면 안그려지는게 생김.

 

23.08.30 추가

layers 순서를 줬음에도 확대/축소 시 안 그려지는 이유를 알았음.

layers그릴 때 동일한 계층에 그려서 생긴 문제임.

 

타 업체의 무료 편집기로 봤을 때 다른 지도들은 레이어를 5개 이상 사용하는 것으로 보였음.

가장 아래에는 베이스 색상, 위에는 땅, 풀, 잔디 등, 또 이 위에는 공원 등 겹겹이 쌓음.

이렇게 그리면 서로 그려지는 영역끼리 침범하지 않음.

 

예를 들자면, 유리를 겹겹이 겹쳐서 만들어내는 예술이 있음.

유리 한 겹, 한 겹 다른 그림들이 그려져 있고 전부 겹치면 입체적인 그림이 완성됨.

여기서 한 개의 레이어는 유리 한 겹임.

유리 한 겹에는 여러 형태가 들어가는데 동일한 층에서는 색이 섞일 수도 있고 덧칠이 될 수도 있으나 다른 층의 유리에는 영향이 없음.

이 상태와 비슷함.

 

내가 원하는 지도가 몇 층인지 생각해보고 레이어배열을 여러 개 만들어야 함.

서로 겹치면 안 되는 대상끼리는 필히 다른 층의 레이어에 넣어주어야 함.

이렇게 만들면 확대/축소해도 원하는 형태로 유지됨.

 

 

끝!

반응형
LIST
댓글
링크
공지사항
최근에 올라온 글