Translate

2020년 10월 29일 목요일

[Leaflet] Extending Leaflet - Handlers and Controls




 - 이 튜토리얼은 Leaflet의 Class 상속이론을 읽었다고 가정한다.

 - Leaflet에서 Map의 움직이는 모든것은 Layer로 되어있다.
 - Control은 (Map컨테이너에 상대적인) 정적인 상태를 유지하는 HTML요소이다.
 - Handler는 Map의 동작을 변경하는 보이지 않는 코드의 일부이다.


핸들러 (Handlers)

 - Map Handler는 Leaflet 1.0의 새로운 개념으로, 브라우저에서 (클릭, 더블클릭, 마우스휠 같은)DOM 이벤트를 처리하고 Map 상태를 변경하는 기능이다.

 - Handler는 상대적으로 간단하다.
 - Map에서 핸들러가 활성화 될때 수행할 addHooks()메소드와 핸들러가 비활성화 될때 수행할 removeHooks() 메소만 있으면 된다.
 - 핸들러의 골격은 다음과 같다.


 - deviceorientation event를 통해 모바일기기가 기울어졌을 때 지도를 이동하는 간단한 핸들러를 보여줄 수 있다.

 - 핸들러는 map.addHandler('tilt', L.TiltHandler) 를 이용하여 지도에 연결시킬 수 있다.
 - 위 코드는 L.TiltHandler의 인스턴스를 map.tilt로 저장할 것이다.
 - 하지만 addInitHook 문법을 사용하여 모든 Map에 핸들러를 연결시키는게 일반적이다.

 - 이제 핸들러는 map.tilt.enable()이 수행되면 활성화되며, map.tilt.disable()이 수행되면 비활성화 된다.

 - 또한 map에 핸들러와 동일한 이름의 속성이 있는경우 해당 옵션을 true로 설정하면 기본적으로 핸들러가 활성화 된다.

 - 이 예제소스를 확인하려면 deviceorientation event를 지원하는 모바일 브라우저가 필요하다. (테스트 시 완벽하지 않고 불안정하니 감안해야 한다.)

(아래 링크를 클릭하여 새창에서 실행되는 페이지를 모바일에서 확인 시 해당 기기의 기울임에 따라서 지도가 이동하는 것을 확인할 수 있다.)

 - 이벤트 유형에 따라 다르지만, Map핸들러는 이벤트리스너를 document, window, L.Map의 컨테이너에 붙일 수 있다.



컨트롤 (Controls)

 - 왼쪽상단 zoom컨트롤, 왼쪽하단 축척, 오른쪽상단 레이어변경 기능에 대해서 지금까지 봐왔을 것이다.
 - 그것들의 중심에는 Map컨테이너 고정위치에 있는 HTML요소인 L.Control이 있다.

 - control을 만드려면 L.Control을 상속받고 onAdd(), onRemove() 메소드를 구현하면 된다.
 - onAdd()메소드가 control에 해당하는 HTML요소의 인스턴스를 반환하는 것을 제외하면 두개의 메소드는 L.Layer와 비슷하게 작동한다. (control이 map에 추가되거나 제거될 때 수행한다)
 - Map에 HTML요소의 추가/제거는 자동으로 이루어 진다.



플러그인 제작

 - 지금까지 모든것을 이해했다면 Leaflet 플러그인을 만들 준비가 된 것이다.
 - 플러그인 명명규칙 및 제작에 대한 몇가지 팁과 우수사례가 있는 PLUGIN-GUIDE.md 파일을 읽어보길 바란다.




참조: https://leafletjs.com/examples/extending/extending-3-controls.html


2020년 10월 28일 수요일

[Leaflet] Extending Leaflet - Layers




 - 이 튜토리얼에서는 Leaflet Class 상속 이론을 읽었다고 가정한다.
 - Leaflet에서 "Layer"는 (map)지도를 이동시킬 때 움직이는 모든것이다.
 - 처음에는 직접 만드는것보다는 간단한 확장 방법을 설명하는게 더 쉽다.


확장 메소드 (Extension methods)

 - 일부 Leaflet 클래스에는 서브클래스의 코드 작성을 위한 진입포인트인 'extension methods'를 가지고 있다.

 - 그중 하나는 L.TileLayer.getTileUrl() 이다.
 - 이 메소드는 L.TileLayer에서 신규 타일이 이미지를 로드해야 할때마다 내부적으로 호출된다.
 - L.TileLayer의 서브클래스를 만들고 getTileUrl() 함수를 다시 작성하여 custom 동작을 만들 수 있다.

 - custom L.TileLayer를 이용하여 PlaceKitten의 랜덤한 고양이 이미지를 노출시켜보자.

 - 일반적으로 getTileUrl()은 타일좌표(coords.x / coords.y / coords.z)를 수신받고 해당 좌표에 대한 타일 (이미지)URL을 생성한다.
 - 이번 예제에서는 좌표를 무시하고 매번 다른 고양이를 얻기위해 난수를 사용한다.



플러그인 코드 분리

 - 이전 예에서는 L.TileLayer.Kitten 과 기본소스와 같은 위치에 정의가 되었다.
 - 플러그인의 경우 플러그인 코드 자체를 파일로 분리하고 해당 플러그인을 사용할 때 import 하는게 좋다.

 - KittenLayer를 L.KittenLayer.js로 분리해주고 사용하는 곳에서 import를 해준다.


L.GridLayer 와 DOM elements

 - 또 다른 확장방법은 L.GridLayer.createTile() 을 이용하는 것이다.
 - L.TailLayer는 (<img> 요소로서) 이미지의 그리드가 있다고 가정하지만, L.GridLayer는 (모든 종류의 HTML Element요소의 그리드를 만들 수 있기에) 그렇게 가정하지 않는다

 - L.GridLayer는 <img>태그의 요소를 생성 가능하지만, <div>/<canvas>/<picture> 등등 다른 모든것들을 생성할 수 있다.
 - createTile()은 전달받은 타일좌표의 HTML Element 인스턴스를 반환하기만 하면 된다.
 - 이때, DOM 요소를 조작하는 방법을 아는것이 중요하다.
 - (Leaflet은 HTML-Element의 인스턴스를 기대하고 있으며, jQuery와 같은 라이브러리의 요소들은 문제가 된다.)

 - custom GridLayer의 예제는 <div>의 타일좌표를 보여주고 있다.
 - 이는 특히 Leaflet 내부를 디버깅하거나 (tile coordinates)타일좌표가 어떻게 작동하는지 이해하는데 유용하다.

 - element가 비동기 초기화를 해야 하는경우 두번째 함수 매개변수인 'done'을 사용하고 (이미지과 완전히 로드된경우)준비되거나 에러가 발생한 경우 해당 함수를 호출한다.


 - custom GridLayer를 사용하면 플러그인은 grid를 만들 때 HTML element를 완전히 컨트롤이 가능하다.
 - 일부 플러그인들은 이미 <canvas>를 사용하여 고급 렌더링을 수행하고 있다.
 - 기본적인 <canvas> GridLayer 사용은 다음과 같다.



픽셀 원점 (The pixel origin)

 - custom L.Layer를 만들 수 있지만, Leaflet이 HTML 요소를 배치하는 것에 대해서 더 깊은 지식이 필요하다.
 - 요약은 아래와 같다.
  1) L.Map 에는 <div>인 (지도 창)'map panes'가 있다.
  2) L.Layer는 'map pane' 안에있는 HTML 요소이다.
  3) map은 모든 LatLng를 지도CRS의 좌표로 변환하고, 그다음 (absolute)절대 '픽셀 좌표'로 변환한다. (CRS의 원점은 픽셀좌표의 원점과 같음)
  4) (중앙 LatLng와 줌레벨을 가지고 있는) L.map이 준비되면 왼쪽 상단의 절대픽셀좌표는 '픽셀원점'이 된다.
  5) 각 L.Layer는 'map pane'에서 픽셀원점과 레이어LatLng의 절대픽셀좌표에 따라 오프셋 된다.
  6) 픽셀원점은 각각 zoomend, viewreset 이벤트가 수행된 이후 리셋되며, (필요한 경우) 모든 L.Layer의 위치는 재계산된다.
  7) map을 주위로 이동 시키는경우 픽셀원점은 리셋되지 않으며, 모든 pane들의 위치가 변경된다.

 - 위 설명은 조금 헷갈릴 수 있으니 다음 지도의 확인이 필요하다.

 - (녹색) CRS원점은 LatLng와 동일하게 유지된다.
 - (빨간색) 픽셀원점은 항상 왼쪽 상단 모서리에서 시작한다.
 - 픽셀원점은 맵이 이동될 때 이리저리 이동하며, ((지도창)map pane이 지도 컨테이너에 따라 재배치됨) 확대/축소 할 시 화면의 동일한 위치에 남아있다. (map pane은 재배치되지 않지만 layer 자체는 다시 그릴 수 있다.)
 - 픽셀원점에 대한 절대픽셀좌표는 확대/축소 할 때 업데이트 되지만 이동중에는 업데이트 되지 않는다.
 - 지도를 확대할 때마다 (녹색 괄호까지의 거리)절대픽셀좌표가 두배가 되는 것을 볼 수 있다.

 - (ex: blue L.Marker)무엇이든 배치하기위해 LatLng는 지도의 L.CRS 안에있는 절대픽셀좌표로 변환된다.
 - 그 다음 픽셀원점의 절대픽셀좌표를 절대픽셀좌표에서 빼고 (연청색)픽셀원점에 대한 오프셋을 제공한다.
 - 픽셀원점은 모든 (지도창)'map pane'의 왼쪽 상단 모서리에 있으므로 이 오프셋을 마커 아이콘의 HTML요소에 적용할 수 있다.
 - (암청색 선)마커의 아이콘앵커는 negative CSS margin을 통해 이루어진다.

 - L.Map.project() 와 L.Map.unproject() 메소드는 절대픽셀좌표에 의해 동작한다.
 - 마찬가지로 L.Map.latLngToLayerPoint() 와 L.Map.layerPointToLatLng() 는 픽셀원점을 기준으로 오프셋으로 작업한다.

 - 다른 레이어들은 이러한 계산을 다른 방식으로 적용한다.
 - L.Marker 는 단순히 아이콘의 위치를 바꾸기만 하면 된다.
 - L.GridLayer는 (절대픽셀좌표의)지도범위를 계산한 다음 요청할 타일좌표 리스트를 계산한다.
 - (polyline, polygon, circle marker 등등)벡터레이어는 각 LatLng를 픽셀로 변환하고, SVG 또는 <canvas>를 사용하여 (기하학적 구조)지오메트리를 그린다.


onAdd 와 onRemove

 - 핵심은 모든 L.Layer는 (지도창)map pane 내부의 HTML 요소이며, 레이어의 코드에 정의된 (position/content)위치와 내용이다.
 - 그러나 레이어가 인스턴스화 되면 HTML요소를 생성할 수 없다.
 - 오히려, 레이어는 지도에 추가되고나서 수행되며, 레이어는 그때까지 map에 대해서 알지 못한다. (심지어 document까지도)

 - 즉, map은 레이어의 onAdd() 메소드를 호출한 후 HTML요소인 레이어를 생성하여 (지도창)map pane에 추가한다.
 - 반대로, 지도에서 레이어가 제거되면 onRemove() 메소드가 호출된다.
 - 레이어는 map에 추가될 때 내용을 업데이트하고 map view가 업데이트 되면 위치를 변경해야한다.
 - 레이어 뼈대는 다음과 같다.

 - 레이어의 HTML요소의 정확한 위치는 (객체를 만들때)레이어의 세부사항에 따라 다르지만, 이 소개는 Leaflet의 레이어코드를 읽고 새로운 레이어를 만드는데 도움이 될 것이다.


부모(클래스)의 onAdd 사용

 - 일부 사용 사례에서는 모든 onAdd 코드를 재생성할 필요는 없지만, (필요시에) 부모의 onAdd를 재사용하는것 대신 일부 세부사항은 초기화 이전 또는 이후에 추가할 수 있다.

 - 예를들어 (옵션을 무시한 ) 항상 빨간색인 L.Polyline의 서브클래스를 가질 수 있다.



참조: https://leafletjs.com/examples/extending/extending-2-layers.html


2020년 10월 26일 월요일

[Leaflet] Extending Leaflet - Class Theory



Leaflet 확장

 - Leaflet에는 수백개의 플러그인이 있다. 이는 Leaflet의 기능을 확장하며 때로는 일반적인 방식으로, 때로는 유스케이스별 방식으로 확장한다.

 - 플러그인이 많은 이유는 Leaflet이 확장하기 쉽기 때문이다.
 - 이 튜토리얼에서는 가장 일반적으로 사용되는 방법을 다룬다.

 - 이 자습서는 다음 사항을 잘 이해하고 있다고 가정한다.
  1) JavaScript
  2) DOM handling
  3) OOP (클래스, 인스턴스, 상속, 메소드 및 프로퍼티와 같은 개념 이해)



Leaflet 아키텍처

 - Leaflet 1.0.0의 단순화된 UML 클래스 다이어그램을 확인해보자.
 - 60개 이상의 JavaScript 클래스가 있어서 다이어그램이 조금 크다.



(마우스 클릭 시 큰 화면으로 확인 가능)


- 기술적 관점에서 Leaflet은 다양한 방법으로 확장 할 수 있다.

 * 가장 일반적인 방법: L.Class.extends()를 활용하여 L.Layer / L.Handler / L.Control 과 같은 서브클래스를 생성

  - map이 이동/확대 될때 Layer가 이동함.
  - Handler는 보이지 않으며 브라우저 이벤트를 해석한다.
  - Control은 고정된 인터페이스 요소이다.

 * L.Class.include() 를 이용하여 기존 클래스에 더 많은 기능을 추가

  - 신규 methods 와 options 추가
  - 특정 methods 변경
  - addInitHook을 사용하여 새로운 (constructor)생성자 코드를 수행

 * L.Class.include() 를 이용하여 기존 클래스의 일부를 변경 (클래스 메소드 작동방법 변경)


 - 이 튜토리얼에서는 Leaflet 1.0.0에서만 사용할 수 있는 일부 클래스와 메소드를 다룬다.
 - 이전 버전용 플러그인을 개발하는 경우 주의해야한다.



L.Class

 - JavaScript는 약간 이상한 언어인데, (Object-Oriented)객체지향 언어가 아니라 (Prototype-Oriented)프로토타입지향 언어이다.
 - 때문에 OOP의미에서 클래스 상속을 사용하기가 어려웠다.

 - Leaflet은 L.Class를 중점적으로 사용하여 클래스 상속을 용이하게 한다.

 - 최근 자바스크립트인 ES6는 클래스를 사용할 수 있지만 Leaflet은 이를 중심으로 설계되지 않았다.


L.Class.extend()

 - Leaflet에서 하위클래스를 만드려면 .extend() 메소드를 사용하면 된다.
 - .extend() 메소드에는 하나의 파라메터를 허용하는데 해당 파라메터는 key-value 쌍으로 되어있는 일반 Object 이다.
 - 해당 Object의 key는 프로퍼티 또는 메소드의 이름이며, value는 프로퍼티값 또는 메소드의 구현체이다.

 - class, method, property 이름을 지정할 때 다음 규칙을 따르는게 좋다.
  1) lowerCamelCase 규칙: Function, method, property, factory 이름
  2) UpperCamelCase 규칙: Class 이름
  3) (접근지정자) private 형식의 property, method 는 밑줄(_)로 시작한다. 이건 private로 만들어 주는것은 아니지만 개발자들에게 직접 사용하지 말라고 권고하는 것이다.


L.Class.include()

 - 클래스가 이미 정의된경우 .include()를 사용하여 기존 property/method 를 재정의 하거나, 새로운 property/method 를 추가할 수 있다. (아래의 코드 참고)


L.Class.initialize()

 - OOP에서 클래스에는 생성자 메소드가 있다.
 - Leaflet 에서 L.Class 생성자 메소드는 항상 initialize 이다.

 - 만약 클래스에 options를 기술한경우 생성자에서 L.setOptions()를 사용해 그것들을 초기화 하는게 좋다.
 - 이 유틸리티 함수는 기술한 options를 클래스의 기본 options 와 병합한다.


 - Leaflet은 특별한 방법으로 options 속성을 처리한다.
 - 부모 클래스의 options는 자식 클래스에 상속된다.


 - 자식클래스가 부모의 생성자를 수행한다음 자신의 생성자를 수행하는게 일반적이다.
 - 이를 위해서 Leaflet 에서는 L.Class.addInitHook() 을 사용할 수 있다.
 - 이 메소드를 이용하여 클래스 initialize() 직후 실행되는 hook 초기화 함수로 사용할 수 있다.

 - 위 코드는 setOptions()를 수행하는 initialize() 이후에 수행된다.
 - 이 뜻은 init hook 이 실행될 때는 this.options 가 이미 생성되어 있다는 의미가 된다.


 - addInitHook은 대체구문이 있으며, 메소드이름과 메소드에 전달할 인수를 채워주면 된다.


부모클래스의 메소드

 - 부모클래스의 메소드를 호출하는 방법은 부모클래스의 prototype에 도달하여 Function.call()을 사용하여 호출할 수 있다.
 - 아래는 L.FeatureGroup의 코드에 대한 샘플이다.

 - 부모의 생성자를 호출하는것도 비슷하지만 다음과 같이 사용이 가능하다.
 - ParentClass.prototype.initialize.call(this, …)


factory

 - 대부분의 Leaflet 클래스는 factory 함수를 가지고 있다.
 - factory 함수는 UpperCamelCase 형식의 클래스명 대신 lowerCamelCase의 클래스명을 사용한다.


명명규칙

 - Leaflet 플러그인의 클래스 이름을 지정할 때 다음의 명명규칙을 따라야 한다.
  1) 플러그인에서 전영변수를 노출시키지 않는다.
  2) 신규 클래스가 있는경우 L 네임스페이스에 직접 추가한다. (ex: L.MyPlugin)
  3) 기존 클래스 중에 하나를 상속하는 경우 하위속성으로 만든다. (ex: L.TileLayer.Banana)



PS. 아래는 해당 튜토리얼에 나와있는 예제



참조: https://leafletjs.com/examples/extending/extending-1-classes.html


2020년 10월 24일 토요일

[Leaflet] Showing video files



웹페이지의 비디오

 - HTML Element의 video 태그를 사용할 수 있을때까지 웹페이지에서 비디오는 어려운 작업이었다.
 - 요즘에는 다음과 같은 HTML 코드를 사용할 수 있다.

 - 이 비디오는 다음과 같이 보여진다.

 - 만약 웹페이지에서 이와같이 보여줄 수 있다면 Leaflet에서 map 안에 보여줄 수 있다.
 - 여기서 비디오를 map과 (fit)크기나 사이즈가 맞게 준비되는 것이 중요하다.
 - 비디오는 지도와 같이 북향 방향으로 되어있어야 하며, 비율은 map과 (fit)맞아야 한다.
 - 그렇지 않으면 이상하게 보일 것이다.



이미지 오버레이의 경계



비디오 오버레이 추가



비디오에 대한 간단한 조작




참고: https://leafletjs.com/examples/video-overlay/


[Leaflet] Working with map panes



What are panes?

 - Leaflet에서 map pane은 개발자가 모르는 사이에 레이어 그룹화를 시킨다.
 - 이 그룹화는 웹 브라우저가 여러 레이어를 개별적으로 작업하는 것보다 더 효율적인 방법으로 작업 할 수 있도록 한다.

 - Map panes는 CSS의 z-index 속성을 이용하여 일부 레이어를 다른레이어 위에 보여준다.
 - 기본 순서는 다음과 같다.
  1) TileLayers and GridLayers
  2) Paths, like lines, polylines, circles, or GeoJSON layers.
  3) Marker shadows
  4) Marker icons
  5) Popups

 - 이 때문에 Leaflet Map에서 팝업이 다른 레이어보다 항상 위에 표시되고 marker는 타일레이어 위에 보여지게된다.

 - (0.7.x에는 없는) Leaflet 1.0.0의 새로운 기능은 이 순서를 사용자 지정할 수 있는 custom map panes 이다.



기본값이 항상 올바른건 아니다.

 - 어떤 특별한 상황에서는 기본 순서가 map에 적합하지 않으며, Cato의 basemaps 과 label 을 통해서 설명을 할 수 있다.


 - Leaflet map에서 basemaps,label 두개의 타일을 이용해서 만들 때, marker나 polygon은 이전의 두개의 타일 위에 나타나겠지만 label타일이 가장 위에 보여지는게 더 나아보일 수 있다.

 - 아래는 기본순서로 보여진 경우(오른쪽)와 기본순서를 custom 한 경우(왼쪽)이다.
   (기본 순서에서는 polygon 이 label 타일보다 위로 올라와 label을 가리게 된다.)



Custom pane



참고: https://leafletjs.com/examples/map-panes/


2020년 10월 23일 금요일

[Leaflet] WMS and TMS




 - 'Web Map Service'의 약자인 WMS는 전문적인 GIS 소프트웨어로 널리 사용된다. (비 GIS사용자는 거의 사용하지 않는다.)
 - 이 형식은 지도 타일과 비슷하지만 더 일반적이며, 웹 지도에서 사용하기에는 적합하지 않다.
 - WMS 이미지는 모서리의 좌표에 의해 정의된다. (Leaflet이 내부적으로 수행하는 계산)

 - TMS는 'Tiled Map Service'를 의미하여 웹지도에 보다 초점을 맞춘 지도 타일링 표준으로, Leaflet이 L.TileLayer에서 기대하는 지도타일과 매우 유사하다.

 - WMTS는 'Web Map Tile Service'를 위한 표준 프로토콜로 L.TileLayer에서 직접 사용할 수 있는 맵 타일을 제공한다.


WMS in Leaflet

 - 누군가 WMS 서비스를 시작하면 해당 서비스는 GetCapabilities 라는 문서에 연결된 가능성이 크다.
 - 이 예제에서는 Mundialis가 제공하는 WMS를 사용한다.
 - 서비스의 기능은 다음 URL에 있다.
 - http://ows.mundialis.de/services/service?request=GetCapabilities

 - Leaflet은 WMS GetCapabilities 문서를 이해하지 못한다.
 - 대신에 L.TileLayer.WMS 레이어를 만들고, 기본적인 WMS URL을 제공하고, 우리가 필요한 WMS옵션을 명시할 수 있다.
 - 기본 WMS URL은 다음과 같이 매개변수가 없는 간단한 GetCapabilities URL이다.
 - http://ows.mundialis.de/services/service?

 - 그리고 Leaflet 지도에서 사용하는 방법은 간단하다.

 - L.TileLayer.WMS 인스턴스에는 최소한 한가지 옵션이 필요하다. (layers)
 - Leaflet의 layer 개념과 WMS의 layer 개념이 다름에 주의가 필요하다.

 - WMS서버는 서비스에서 layers의 설정을 정의한다.
 - 이러한 내용은 GetCapabilities XML 문서에 정의되어 있지만, 대부분의 경우 지루하고 이해하기 어렵다.
 - 일반적으로 QGIS와 같은 소프트웨어를 사용하여 WMS서버에서 사용할 수 있는 layers를 확인하여 사용가능한 레이어 이름을 확인하는것이 좋다.


 - 우리는 Mundialis WMS가 TOPO-OSM-WMS라는 이름의 WMS 레이어를 베이스맵으로 하고 있음을 알 수 있다.
 - 아래의 코드를 통해 확인을 해보자.


SRTM30-Colored-Hillshade WMS layer 로하면 다음과 같다.


 - layers 옵션은 쉼표로 구분된 layers 목록이다.
 - WMS 서비스가 여러개의 layers를 정의한경우, 지도 이미지 요청은 두개 이상의 layer를 참조할 수 있다.

 - 우리가 사용하는 WMS서버의 예로는 세계 지형을 보여주는 'TOPO-WMS' WMS layer와 지역의 이름을 보여주는 'OSM-Overlay-WMS' WMS layer가 있다.

 - WNS서버는 쉼표로 구분된 2레이어를 요청 시 하나의 이미지로 구성한다.


 - WMS서버에 하나의 이미지를 요청한다는 점에 유의해야한다.
 - 이것은 지형을 위해 L.TileLayer.WMS를 생성하고 장소를 위한 하나의 L.tileLayer.WMS를 생성해서 둘다 지도에 추가하는것과는 다르다.

 - 첫번째의 경우, 1개의 이미지요청을 하고 WMS서버가 이미지를 어떻게 구성할지 결정한다. (이미지를 서로 위에 올려놓음)
 - 두번째의 경우 두개의 이미지 요청을 하고, 웹브라우저의 Leaflet 코드가 어떻게 구성할지 결정한다.

 - layers control을 이용하여 결합하여 간단히 지도를 생성하면 어떻게 다른지 알 수 있다. (아래의 예제코드)
 - 'Topography, then places' 옵션을 선택 시 장소가 지역의 위로 올라오는 것을 볼 수 있다.
 - layers를 많이 요청 시 레이어를 구성하는 방법은 WMS서버에게 달려있다.


WMS서비스의 GIS사용자를 위한 참고사항

 - GIS관점에서 보면 Leaflet의 WMS처리는 상당히 기본적이며 GetCapabilities / GetFeatureInfo / legend의 지원이 없다.

 - T.TileLayer.WMS는 추가적인 옵션이 있는데 Leaflet API Document에서 찾아볼 수 있다.
 - API Document에 기술되지 않은 모든 옵션은 getImage URL의 WMS서버로 전송된다.

 - 또한 Leaflet은 CRS:3857, CRS:3395 and CRS:4326 등 매우 적은수의 좌표시스템을 지원한다. (document의 L.CRS 참고)
 - WMS서비스가 해당 좌표계의 이미지를 제공하지 않는경우, Proj4Leaflet을 사용하여 Leaflet의 다른 좌표시스템을 사용해야 할 수 있다.
 - 그외에는 지도를 초기화 할때 올바를 CRS를 사용하면 모든 추가된 WMS layers는 사용가능하다.


TMS in Leaflet

 - Leaflet은 TMS서비스를 공식적으로 지원하지 않지만 tile 이름구조는 공통 L.TileLayer 이름체계와 유사하며 평범한 TMS서비스를 표시할 수 있다.

 - 아래와 같은 엔드포인트가 있는 TMS서버를 고려해보자.
 - http://base_url/tms/1.0.0

 - TMS 및 TMS 사양에 대한 MapCache 도움말을 확인하면 TMS의 지도 타일 URL이 다음과 같다.
 - http://base_url/tms/1.0.0/ {tileset} / {z} / {x} / {y} .png

 - L.TileLayer로 TMS서비스를 사용하려면, 사용가능한 tileset에 대해 기능문서를 체크하여 아래와 같은 base URL을 빌드할 수 있다. (우리의 경우 http://base_url/tms/1.0.0로 기본 엔드포인트가 같을때)
 - http://base_url/tms/1.0.0/{example_layer}@png/{z}/{x}/{y}.png

 - 아래와 같이 레이어를 인스턴스화 할때 tms:true 옵션을 사용해라.


 - Leaflet 1.0의 새로운 기능은 tms:true 옵션 대신 URL에 {-y}를 사용할 수 있다.


 - (Leaflet 0.7에서) tms:true 옵션이나 (Leaflet 1.0에서) {-y}는 일반적인 L.TileLayer 좌표 원점이 왼쪽 상단 모서리에 있으므로 Y좌표가 내려가기 때문에 필요한 옵션과 기능이다.
 - TMS에서 좌표의 원점은 왼쪽 하단 모서리가 되어 Y좌표가 위로 올라가게 된다.

 - Y좌표와 tilesets의 발견의 차이 외에도 TMS서비스는 L.TileLayer가 기대하는 방법으로 정확하게 tile을 제공한다.


참조: https://leafletjs.com/examples/wms/wms.html


[Leaflet] Non-geographical maps



지구가 아닌경우

 - 때때로 지도는 지구 표면의 사물을 나타내지 않으며 이와같이 지리적 위도/경도에 대한 개념이 없는 경우가 있는데, 대부분 게임지도와 같은 경우이다.

 - 이번 튜토리얼에서는 아래의 스타컨트롤2의 startmap 을 예제 지도이미지로 사용한다.


(클릭 시 예제에 사용된 원본 이미지 확인 가능하다.)



 - 이 게임은 코너에서 볼 수 있듯이 사각 좌표계가 내장되어 있어 좌표계를 수립할 수 있다.



CRS.Simple

 - CRS는 좌표 참조 시스템을 의미하며, 좌표 벡터에서 좌표가 무엇을 의미하는지 설명하기 위해 지리학자들이 사용하는 용어다.

 - 예를 들어, [15, 60]은 지구에서 위도 경도를 사용할 경우 인도양의 한 지점을 나타내며, 또는 태양계 Krueger-Z는 우리의 starmap에서 볼 수 있다.



CRS.Simple map의 일반적인 문제점

 - Leaflet CRS의 기본값은 CRS.Earth이며, 360도의 경도는 256가로픽셀과 매핑되고 약170도 위도는 256세로픽셀과 매핑된다.(결국 정사각형이며, 줌 레벨 0인경우에 해당된다.)

 - CRS에서 하나의 수평지도 단위는 하나의 수평픽셀에 매핑되며, 수직부분도 마찬가지다.

 - map의 전체 부분은 1000x1000 픽셀크기이며, 따라서 우리의 HTML 컨테이너에 들어맞지 않는다.

 - 다행히 minZoom 값을 0 이하로 설정할수 있다.


Pixels VS Map Units

 - 한가지 일반적인 실수가 있는데, CRS.Simple를 사용할 때 'map units'와 'image pixels'와 같다고 가정하는 것이다.

 - 이 경우에 지도는 1000x1000 단위(unit)를 커버하지만 이미지는 2315x2315픽셀이다.

 - 상황에따라 '1 pixel = 1 map unit' 또는 '64 pixel = 1 map unit' 또는 다른 방법이 요구될 수 있다.

 - 그리드의 map units에 맞춰서 레이어를 추가하는 식으로 생각해야 한다. (L.ImageOverlays, L.Markers 등등)

 - 사실 우리가 사용하고 있는 이미지는 1000개의 map units을 커버하고 있다. (상당한 여백이 있다.)

 - 0과 1000 사이에 몇개의 픽셀이 있는지 측정하고 추론하면 이 이미지에 대한 올바른 좌표 경계를 알 수 있다.


이 LatLng는 당신이 알고있는 그것이 아니다.

 - Sol의 좌표가 [175,145] 대신에 [145,175]로 되어있고, map center 또한 이렇게 되어있는것을 눈치 챘을 것이다.

 - CRS.Simple의 좌표는 [x,y] 대신에 [y,x]를 사용하고 있으며 Leaflet 또한 [lng,lat] 대신에 [lat,lng]를 사용하고 있다.

 - [lng,lat] or [lat,lng] or [y,x] or [x,y] 에 대한 논쟁은 새로운게 아니라 명확히 합의된게 없다.

 - 또한 Leaflet의 L.Coordinate 이름 대신 L.LatLng 라는 클래스명을 사용한 것에 대해서도 합의가 부족하다.

 - L.LatLng 라는 이름으로 [y,x] 좌표로 작업하는게 그다지 이해되지 않는다면 쉽게 래퍼를 만들어 사용할 수 있다.



참고: https://leafletjs.com/examples/crs-simple/crs-simple.html


2020년 10월 21일 수요일

[Leaflet] Zoom levels



Zoom Levels

 - Leaflet은 위도, 경도 그리고 zoom level로 작동을 한다.

 - 줌 레벨이 낮으면 지도가 대륙 전체를 표시하게되며, 줌 레벨이 높으면 지도가 도시의 세부 모습을 표시하게 된다.

 - 줌 레벨의 작동방식을 이해하려면 먼저 측지학에 대한 기본 지식이 필요하다.



지구의 모양

 - 줌 레벨 0으로 잠긴 간단한 지도를 살펴보자.



"전체 지구"는 가로 256픽셀, 세로 256픽셀 이미지 하나일 뿐이다.


하지만 진짜 지구는 사각형이 아니다. 오히려 지구는 구형과 비슷한것과 비슷한 불규칙한 모양을 가지고 있다.


그래서 우리는 지구가 대부분 둥글다고 가정한다. 평평하게 만들기 위해 가상의 원통을 주위에 놓고 펴고서 정사각형으로 보이도록 자른다.


위 방법은 평면에서 지구 표면을 표시하는 유일한 방법이 아니며, 장점과 단점이 다른 수백가지의 방법이 있다. 아래 6분짜리 비디오는 주제에 대해서 잘 설명해 주고 있다.
(한국어 자막이 있어 보기 좋다.)

측지학, 지도투영 및 좌표계와 같은 것은 많이 어렵다.(이 튜토리얼의 범위를 벗어남)

지구가 정사각형이라고 가정하는것이 항상 옮은것은 아니지만 대부분의 경우 충분히 잘 작동하고 일이 더 간단해지며 Leaflet (및 기타 지도 라이브러리)의 속도를 높일 수 있다.



2의 거듭제곱

지금은 지구가 정사각형 이라고 가정해 보겠다.

zoom level 0 에서 지구를 표현하면 가로/세로가 256 픽셀이다.

zoom level 1로 변경하면 가로/세로가 두배가 되고 256x256 픽셀 이미지 4대로 표현될 수 있다.


각 zoom level은 4개의 타일로 분할되고 크기가 2배가 되어 면적이 4배가 된다.
 - 가장자리의 길이는 tileSize 옵션으로 제공된다.
 - 지구의 가로/세로 길이는 256 * 2(zoomlevel) 픽셀이 된다.


이러한 줌은 계속되어 타일 서비스는 적용 범위에 따라 zoom level 18 까지 제공되며, 이정도면 타일당 몇개의 도시 블록을 보기에 충분하다.



(Scale) 규모에 대한 참고사항

원통형 투영을 사용할 때의 단점 중에 하나는 배율이 일정하지 않고 특히 낮은 zoom level 에서 거리나 크기를 측정할 수 없다는 것이다.

기술적 용어로, 리플릿이 사용하는 원통형 투영은 등치형(모형을 보존함)이지만 등치형(거리를 보존하지 않음)은 아니며, 등치영역(적도 근처의 사물이 지금보다 작게 나타나므로 영역을 보존하지 않음)은 아니다.

지도에 L.Control.Scale 을 추가하여 적도에서 60도 북쪽으로 이동 시 축척 비율이 두배가 되는 것을 볼 수 있다.



zoom 컨트롤

leaflet 지도는 zoom 레벨을 컨트롤 하는 여러가지 방법이 있지만, 가장 확실한 방법은 setZoom() 을 사용하는 것이다.
 - map.setZoom(0); 을 실행 시 zoom레벨이 0으로 셋팅된다.


zoom을 셋팅하는 다른 방법은 아래와 같다.

1. setView(center, zoom) - 지도 중심을 설정한다.

2. flyTo(center, zoom) - setView와 비슷하지만 부드러운 애니메이션으로 이동한다.

3. zoomIn()/zoomIn(delta) - delta 값의 zoom 확대 (기본값: 1)

4. zoomOut()/zoomOut(delta) - delta 값의 zoom 축소 (기본값: 1)

5. setZoomAround(fixedPoint, zoom) - 포인트를 고정하면서 zoom 레벨을 설정한다. (마우스 스크롤을 이용하여 zoom레벨 설정과 같다)

6. fitBounds(bounds) - 지도의 직사각형 영역에 맞게 zoom을 자동으로 계산한다.



(분수값)부분 확대(Fractional zoom)

Leaflet 1.0.0에 도입된 기능은 부분확대 개념이다. 그 전에는 zoom 레벨이 정수(0, 1, 2 등) 이었지만 이제는 1.5나 1.25와 같은 분수를 사용할 수 있다.

부분 zoom 기능은 기본적으로 비활성화 되어있으며, 활성화 하려면 지도의 zoomSnap 옵션을 사용하면 된다.
 - zoomSnap 기본값은 1이며, 값은 0, 1, 2와 같은 값도 셋팅 가능하다.
 - zoomSnap을 0.5로 설정 시 zoom레벨은 0 / 0.5 / 1 / 1.5 이런식이 될 것이다.
 - zoomSnap을 0.1로 설정 시 zoom레벨은 0 / 0.1 / 0.2 와 같이 될 것이다.


Leaflet은 zoom레벨은 가장 가까운 유효한 zoomSnap 값으로 이동한다.
 - 예를들어 zoomSnap 0.25 로 설정 후 map.setZoom(0.8) 수행 시 0.07로 zoom레벨이 셋팅된다.
 - 이 상황은 map.fitBounds(bounds) 와 스크린에 손가락 제스쳐를 이용해 zoom레벨 변경시에도 동일하다.

zoomSnap을 0으로 맞출 수 있는데 이런경우 zoom레벨 변경 시 zoomSnap 값에 영향을 받지 않게된다.

추가적으로 zoomSnap과 관련있는 중요한 zoomDelta 옵션이 있다.
 - 이 옵션은 zoom 버튼을 눌러 zoom in/out 동작 시 zoom레벨을 얼마나 변화할지에 대한 설정이다.
 - 키보드 + / - 버튼을 누를 때도 마찬가지다.


마우스 휠의 zoom컨트롤은 wheelPxPerZoomLevel 옵션으로 zoom 확대/축소 속도 조절이 가능하다.


아래의 예제를 보고 다음의 zoom 레벨의 변화를 확인할 수 있다.

 * 터치스크린의 핀치줌 (손가락 확대/축소)

 * 마우스 휠을 이용한 확대/축소

 * 박스 zoom (키보드 Shift 키를 누른상태에서 마우스 드래그)

 * 왼쪽 위 확대/축소 버튼



참고: https://leafletjs.com/examples/zoom-levels/


[Leaflet] Layer Groups and Layers Control



Layer Groups and Layers Control

 - 이 튜토리얼은 여러 레이어를 하나로 그룹화하는 방법과 사용자가 맵에서 다른 레이어로 쉽게 전환할 수 있도록 레이어 컨트롤 사용하는 방법을 기술한다.




참고: https://leafletjs.com/examples/layers-control/


[Leaflet] Interactive Choropleth Map



인터랙티브 등치 지역도

이번에는 GeoJSON과 사용자지정컨트롤을 이용하여 미국 주 인구밀도의 인터랙티브 등치 지역도를 만들어 본다.



데이터 소스

 - 미국의 주당 인구밀도의 시각화를 만들기 위해 가장 편리하고 간단한 저장 및 표시방법은 GeoJSON 이다.

 - (us-states.js 샘플파일) GeoJSON 데이터의 (일부)기능은 아래와 같으며 전체 GeoJSON데이터는 페이지 제일 하단에 있다.


기본 상태지도


색상 추가

 - 이제 인구밀도에 따라 주를 색칠한다. 색상은 ColorBrewer 사이트의 도움을 받는다.


인터랙션 추가


커스텀 컨트롤 (Info)


커스텀 컨트롤 (Legend)


전체 데이터 소스




참고: https://leafletjs.com/examples/choropleth/


2020년 10월 20일 화요일

[Leaflet] Using GeoJSON



GeoJSON 정보

GeoJSON 스펙(RFC 7946) 링크

 GeoJSON은 다양한 지리 데이터 구조를 인코딩하기위한 형식이다.

 GeoJSON 객체는 공간영역(Geometry), 공간경계(Feature), 기능리스트(FeattureCollection)을 나타낼 수 있다.

 GeoJSON은 Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, GeometryCollection에 대한 geometry 타입을 지원한다.

 GeoJSON의 Feature에는 Geometry객체, 추가속성이 포함되고, FeatureCollection 에는 기능 리스트들이 포함되어 있다.

 * Leaflet은 위의 모든 GeoJSON 타입을 지원하며, properties를 셋팅하고 features를 기술할 시 Features/FeatureCollection를 가장 잘 작동한다.


* 추가 GeoJSON 참고 URL: https://ko.wikipedia.org/wiki/GeoJSON



GeoJSON 레이어


옵션


스타일



pointToLayer


onEachFeature


Filter


종합


참고: https://leafletjs.com/examples/geojson/


[Leaflet] Markers with Custom Icons



마커와 커스텀 아이콘



출처: https://leafletjs.com/examples/custom-icons/


[Leaflet] On Mobile



모바일 및 Geolocation

참고: https://leafletjs.com/examples/mobile/

2020년 10월 18일 일요일

[Leaflet] Quick Start Guide



Leaflet 튜토리얼


설명은 코드에 주석으로 추가함.


1004lucifer

1. 페이지 준비 및 지도설정




2. 마커, 원 및 다각형



3. 팝업



4. 이벤트



참고: https://leafletjs.com/examples/quick-start/