Coder Social home page Coder Social logo

lvisei / leaflet-demo Goto Github PK

View Code? Open in Web Editor NEW
120.0 5.0 70.0 63.04 MB

Getting started with Leaflet's small demo

License: MIT License

HTML 32.38% JavaScript 46.22% CSS 2.81% Less 18.59%
leaflet-demo demo-map leaflet javascript web-map leaflet-api esri-leaflet leaflet-arcgis

leaflet-demo's Introduction

入门 Leaflet 之小 Demo

写在前面

WebGIS 开发基础之 Leaflet

1. GIS Web开发基本概念:

GIS、Map、Layer、Feature、Geometry、Symbol、Data(Point、Polyline、Polygon)、Renderer、Scale、Project、Coordinates;

2. GIS Web 开发概述:

架构模式、常用平台和 SDK、二维、三维

3. 使用 Leaflet 开发常用功能:

  • 地图加载(底图类型、切换)
  • 地图操作(缩放、平移、定位/书签、动画)
  • 图层管理(加载、移除、调整顺序)
  • 要素标绘(点/聚簇、线、面,符号化/静态动态)
  • 属性标注(字段可选、样式定制)
  • 专题地图(点、线、面,渲染)
  • 查询定位(属性查询、空间查询/周边搜索/缓冲区/面查点线面/点线查面、图属互查、综合查询)
  • 信息窗口(入口、Popup、定制)
  • 坐标转换(地理与投影、不同地理坐标系)
  • 空间运算(长度面积测量、点取坐标、缓冲区、相交包含关系)
  • 动态监控(固定点状态切换、车辆监控)

4. Leaflet 常用 API

Leaflet API

Demo 用到的库

  • Flat-UI - 基于 Bootstrap 的一个扁平化风格 web 开发框架。
  • Leaflet - 一个为建设交互性好适用于移动设备地图,而开发的现代的、开源的 JavaScript 库。
  • Esri Leaflet - 一个轻量级的工具包,基于 leaflet 利用 ArcGIS 服务。

PART 1: 地图加载(底图类型、切换) Demo 1

  1. 库引用
<link rel="stylesheet" type="text/css" href="./lib/Flat-UI-master/dist/css/vendor/bootstrap/css/bootstrap.min.css"/>
<link rel="stylesheet" href="./lib/Flat-UI-master/dist/css/flat-ui.min.css">
<link rel="stylesheet" href="./lib/leaflet/leaflet.css">
<script src="./lib/Flat-UI-master/dist/js/vendor/jquery.min.js"></script>
<script src="./lib/Flat-UI-master/dist/js/flat-ui.js"></script>
<script src="./lib/leaflet/leaflet.js"></script>
<script src="./js/urlTemplate.js"></script>
  1. 地图加载与切换
const map = L.map('mapDiv', {
  // 要使用的坐标参考系统,默认的坐标参考系,互联网地图主流坐标系
  crs: L.CRS.EPSG3857,
  // WGS84 坐标系,GPS 默认坐标系
  // crs: L.CRS.EPSG4326,
  zoomControl: true,
  // minZoom: 1,
  attributionControl: false,
}).setView([31.626866, 104.152894], 18);
// 定位在成都北纬 N30°37′45.58″ 东经 E104°09′1.44″

let baseLayer = L.tileLayer(urlTemplate.mapbox_Image, {
  // 最大视图
  maxZoom: 17,  
  // 最小视图
  minZoom: 2,   
  attribution:
  '@lvisei  &copy; <a href="https://github.com/lvisei/leaflet-demo">leaflet-demo</a>',
}).addTo(map);

const setLayer = (ele) => {
  map.removeLayer(baseLayer);

  if (ele == 'mapbox_Image') {
    baseLayer = L.tileLayer(urlTemplate.mapbox_Image, {
      maxZoom: 17,
      minZoom: 2,
    }).addTo(map);
  } else if (ele == 'mapbox_Vector') {
    baseLayer = L.tileLayer(urlTemplate.mapbox_Vector, {
      maxZoom: 17,
      minZoom: 1,
    }).addTo(map);
  }
};

PART 1.1:基于 Demo 1 利用 H5 Geolocation API 定位到当前位置 Demo 1.1

gif

  1. 库引用 如上 Demo 1
<!-- marker高亮显示库引用 -->
<link rel="stylesheet" href="./lib/leaflet.marker.highlight/leaflet.marker.highlight.css">
<script src="./lib/leaflet.marker.highlight/leaflet.marker.highlight.js"></script>
  1. 判断浏览器是否支持
let map;
let baseLayer;
// 使用H5 API定位 定位在当前位置
if (navigator.geolocation) {
  console.log('/* 地理位置服务可用 */');
  navigator.geolocation.getCurrentPosition(h5ApiSuccess, h5ApiError);
} else {
  console.log('/* 地理位置服务不可用 */');
  // 指定一个数据 定位在成都北纬N30°37′45.58″ 东经E104°09′1.44″
  mapInit([30.626866, 104.152894]); 
}
  1. 定位成功或失败处理方法
const h5ApiSuccess = (position) => {
  // 纬度
  const latitude = position.coords.latitude;
  // 经度
  const longitude = position.coords.longitude; 
  console.log('你的经度纬度分别为' + longitude + ',' + latitude + '。');
  return mapInit([latitude, longitude]);
};

const h5ApiError = () => {
  console.log('/* 地理位置请求失败 */');
  // 指定一个数据 定位在成都北纬N30°37′45.58″ 东经E104°09′1.44″
  mapInit([31.626866, 104.152894]);
};
  1. 成功后初始化底图
const mapInit = (LatLng) => {
  map = L.map('mapDiv', {
    // 要使用的坐标参考系统,默认的坐标参考系,互联网地图主流坐标系
    crs: L.CRS.EPSG3857,
    // WGS84 坐标系,默认坐标系
    // crs: L.CRS.EPSG4326,
    zoomControl: true,
    // minZoom: 1,
    attributionControl: true,
    // 定位在当前位置
  }).setView(LatLng, 18); 

  baseLayer = L.tileLayer(urlTemplate.mapbox_Image, {
    // 最大视图
    maxZoom: 17, 
    // 最小视图
    minZoom: 2, 
    attribution:
    '@lvisei &copy; <a href="https://github.com/lvisei/leaflet-demo">leaflet-demo</a>',
  }).addTo(map);

  L.marker(LatLng, {
    // 永久高亮显示
    highlight: 'permanent',
  }).addTo(map);
};
  1. 更多内容
  • 更多了解 geolocation 对象,可参考 MDN Web 文档
  • 更多了解使用 marker 高亮显示,可参考 leaflet.marker.highlight 插件
  • 基于 Demo 1 利用 leaflet 封装好的 H5 定位 API,定位到当前位置 Demo

PART 2: 地图操作(缩放、平移、定位/书签、动画) Demo 2

gif

  1. 库引用 如上 Demo 1

  2. 设置地图缩放到指定图层

const setZoom = () => {
  // 设置地图缩放到
  map.setZoom(10, {
    // animate: false
  });
};
  1. 图层往里进一个图层,放大
const setZoomIn = () => {
  // 图层往里进一个图层,放大
  map.zoomIn(); 
};

const setZoomOut = () => {
  // 图层往里出一个图层,缩小
  map.zoomOut(); 
};
  1. 地图平移至中心点
const panTo = () => {
  // 地图平移,默认就是true,将地图平移到给定的中心。如果新的中心点在屏幕内与现有的中心点不同则产生平移动作。
  map.panTo([37.91082, 128.73583], {
    animate: true,
  }); 
};
  1. 地图飞到中心点
const flyTo = () => {
  // 点到点的抛物线动画,平移加缩放动画
  map.flyTo([36.52, 120.31]);
};

注意:尽量避免 setZoom()等地图缩放方法与 flyTo、flyToBounds 一起合用,因为这两类地图操作方法都有各自的缩放值,造成动画不流畅、不能定位到目的点。

  1. 地图飞到边界的合适的位置
const flyToBounds = () => {
  // getBounds(获取边界):返回地图视图的经纬度边界。
  map.flyToBounds(polygon.getBounds()); 
  // 飞到这个多变形区域上面,自动判断区域块的大小,合适缩放图层,将地图视图尽可能大地设定在给定的地理边界内。
};

let polygon = L.polygon(
  [
    [37, -109.05],
    [41, -109.03],
    [41, -102.05],
    [37, -102.04],
  ],
  [40.774, -74.125],
  {
    color: 'green',
    fillColor: '#f03',
    fillOpacity: 0.5,
  }
).addTo(map); 
// 地图上绘制一个多边形
  1. 地图定位到边界的合适的位置
const fitBounds = () => {
  // getBounds(获取边界):返回地图视图的经纬度边界。
  console.log(polygon.getBounds());
  map.fitBounds(polygon.getBounds()); 
  // 平移到一个区域上面,自动判断区域块的大小,合适缩放图层
};

let polygon = L.polygon(
  [
    [37, -109.05],
    [41, -109.03],
    [41, -102.05],
    [37, -102.04],
  ],
  [40.774, -74.125],
  {
    color: 'green',
    fillColor: '#f03',
    fillOpacity: 0.5,
  }
).addTo(map); 
// 地图上绘制一个多边形

PART 3: 图层管理(加载、移除、调整顺序): Demo 3

2018-02-28_223709

  1. 库引用
<link rel="stylesheet" type="text/css"  href="./lib/Flat-UI-master/dist/css/vendor/bootstrap/css/bootstrap.min.css"
    />
<link rel="stylesheet" href="./lib/Flat-UI-master/dist/css/flat-ui.min.css">
<link rel="stylesheet" href="./lib/leaflet/leaflet.css">
<script src="./lib/Flat-UI-master/dist/js/vendor/jquery.min.js"></script>
<script src="./lib/Flat-UI-master/dist/js/flat-ui.js"></script>
<script src="./lib/leaflet/leaflet.js"></script>
<!-- esri-leafleat插件 -->
<script src="./lib/esri-leaflet-v2.1.2/dist/esri-leaflet.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/proj4js/2.6.2/proj4.js"></script>
<script src="./js/urlTemplate.js"></script>
  1. 使用 esri-leaflet 插件加载 ArcGIS 底图服务
let oMap = null;
let oLayer = [];

oMap = L.map('mapDiv', {
  crs: L.CRS.EPSG4326,
  zoomControl: false,
  minZoom: 7,
  attributionControl: false
  // 定位在重庆
}).setView([29.59, 106.59], 12);

oLayer.push(L.esri.tiledMapLayer({
  url: urlTemplate.SYS_IMG_MAPSERVER_PATH,
  maxZoom: 17,
  minZoom: 0,
  // 是否浏览器在跨域的情况下使用GET请求
  useCors: false,
}).addTo(oMap)); 
// 加载第一个底图

oLayer.push(L.esri.tiledMapLayer({
  url: urlTemplate.SYS_IMG_LABEL_MAPSERVER_PATH,
  maxZoom: 17,
  minZoom: 0,
  useCors: false,
}).addTo(oMap));
// 加载第二个底图
  1. 切换底图(移除及加载)
const setLayer = (layerUrls, maxZoom) => {
  for (let i = 0; i < oLayer.length; i++) {
    // 将图层在地图上移除
    oMap.removeLayer(oLayer[i]); 
  }
  // 制空数组
  oLayer = [];
  layerUrls.map((item) => {
    oLayer.push(
      L.esri
      .tiledMapLayer({
        url: item,
        // 是否浏览器在跨域的情况下使用GET请求
        useCors: false,
        maxZoom: maxZoom,
      })
      .addTo(oMap)
    );
  });
};

不同的底图可能图层数不一样,就可能造成浏览器去请求不存在的图层,以及给用户展示出空白区域的不好体验,所以切换图层时候应注意设置最大及最小缩放值。

PART 4: 要素标绘(点、线、面,符号化/静态动态) Demo 4

gif

  1. 库引用 如上 Demo 1

  2. 画一个圆

// 画一个 circle
const circle = L.circle([36.52, 120.31], {
  // 描边色
  color: 'green',
  // 填充色
  fillColor: '#f03', 
  // 透明度
  fillOpacity: 0.5,
  // 半径,单位米
  radius: 10000
}).addTo(map);
// 绑定一个提示标签
circle.bindTooltip('我是个圆');
  1. Maker 及自定义 Maker
// 生成一个 maker
const marker = L.marker([36.52, 120.31]).addTo(map);
// 绑定一个提示标签
marker.bindTooltip('这是个Marker', { direction: 'left' }).openTooltip();


// 自定义一个  maker
const greenIcon = L.icon({
  iconUrl: './icon/logo.png',
  // size of the icon
  iconSize: [300, 79],
  // point from which the popup should open relative to the iconAnchor
  popupAnchor: [0, -10] 
});

const oMarker = L.marker([36.52, 124.31], { icon: greenIcon }).addTo(map);
// 绑定一个提示标签
oMarker.bindTooltip('这是个自定义Marker', { direction: 'left', offset: [-150, 0] });
  1. 画一根线
// 画一根线
const polyline = L.polyline([[45.51, -122.68], [37.77, -122.43], [34.04, -118.2]], { color: 'red' }).addTo(map);
// 飞到这个线的位置
map.fitBounds(polyline.getBounds());
  1. 画一个多边形
// 画一个polygon
const polygon = L.polygon([
  [[37, -109.05], [41, -109.03], [41, -102.05], [37, -102.04]], // outer ring
  [[37.29, -108.58], [40.71, -108.58], [40.71, -102.50], [37.29, -102.50]] // hole
], {
    color: 'green',
    fillColor: '#f03',
    fillOpacity: 0.5
  }).addTo(map);
// 绑定一个提示标签
polygon.bindTooltip('this is 个多边形');
// 飞到这个多边形的位置
map.fitBounds(polygon.getBounds());

PART 5: 信息窗口(入口、Popup、定制) Demo 5

ezgif com-video-to-gif03

  1. 库引用 如上 Demo 1

  2. 画一个 circle 并绑定一个 Popup

// 画一个circle
const circle = L.circle([36.92, 121.31], {
 color: 'green', //描边色
 fillColor: '#f03',  //填充色
 fillOpacity: 0.5, //透明度
 radius: 10000 //半径,单位米
}).addTo(map);

// 绑定一个弹窗
circle.bindPopup('我是个圆');
  1. 定位一个 marker,绑定一个自定义 Popup
// 定位一个maker
const marker = L.marker([36.52, 120.31]).addTo(map);

// maker上自定义一个 popup
const html = '<p>Hello world!<br />This is a nice popup.</p>';

const popup = marker.bindPopup(html, { maxHeight: 250, maxWidth: 490, className: 'content', offset: [0, 0] }).on('popupopen', function (params) {
  console.log(params)
});
  1. 实现动态改变 Popup 的内容
const mypop = L.popup();

map.on('click', function (e) {
  mypop.setLatLng(e.latlng)
    .setContent('你临幸了这个点:<br>' + e.latlng.toString())
    .openOn(map);
});

PART 6: geojson 数据绘制边界(坐标转换、渲染) Demo 6

gif

  1. 库引用 如上 Demo 3

  2. 获得 geojson 并处理数据

// 请求 geojson 并处理数据
const population = () => {
  $.get('./js/geojson.json', function (response) {
    const poplData = response.data;
    const PolygonsCenter = response.geopoint;
    drawPolygons(poplData, PolygonsCenter);
  });
};

Mock 返回的数据 GeoJSON

  1. 绘制边界并添加图例
let oPolygon_VilPop = [];

const legend = L.control({
  position: 'bottomright'
});

// 绘制边界
const drawPolygons = (poplData, PolygonsCenter) => {
  for (const i in poplData) {
    poplData[i].geoJson = JSON.parse(poplData[i].geoJson);
    oPolygon_VilPop[i] = L.geoJSON(poplData[i].geoJson, {
      style: function () {
        return {
          color: 'white',
          // 获取边界的填充色
          fillColor: getBgColor(poplData[i].population), 
          fillOpacity: 0.6,
          weight: 3,
          dashArray: '10',
        };
      },
    })
      .bindTooltip(poplData[i].villageName + '<br><br>人口' + poplData[i].population + '人', {
      direction: 'top',
    })
      .on({
      // 鼠标移动上去高亮
      mouseover: highlight,
      // 鼠标移出恢复原样式
      mouseout: resetHighlight, 
      // 点击最大化
      click: zoomTo, 
    })
      .addTo(oMap);
  }

  // 添加图例
  legend.onAdd = legendHtml;
  legend.addTo(oMap);

  // 定位到该界限的中心位置
  oMap.flyToBounds(PolygonsCenter);
};
  1. 返回边界的填充色及图列的样式
const getBgColor = (d) => {
  return d > 400
    ? '#800026'
  : d > 300
    ? '#BD0026'
  : d > 200
    ? '#FC4E2A'
  : d > 100
    ? '#FD8D3C'
  : d > 50
    ? '#FED976'
  : '#FFEDA0';
};

const legendHtml = (map) => {
  let div = L.DomUtil.create('div', 'legend locateVP_legend'),
        grades = [0, 50, 100, 200, 400],
        labels = [],
        from,
        to;
  for (const i = 0; i < grades.length; i++) {
    from = grades[i];
    to = grades[i + 1];
    labels.push(
      '<i style="background:' + getBgColor(from + 1) + '"></i> ' + from + (to ? ' &sim; ' + to + '人' : '以上')
    );
  }
  div.innerHTML = labels.join('<br>');
  return div;
};
  1. 鼠标移动上去的事件、鼠标移出的事件、发生点击的事件
const highlight = (e) => {
  const layer = e.target;
  layer.setStyle({
    weight: 6,
    color: '#fff',
    fillOpacity: 0.9,
    dashArray: '0',
  });
};

const resetHighlight = (e) => {
  const layer = e.target;
  layer.setStyle({
    color: 'white',
    weight: 3,
    fillOpacity: 0.6,
    dashArray: '10',
  });
};

const zoomTo = (e) => {
  oMap.fitBounds(e.target.getBounds());
};

写在后面

国内常用地图服务资源加载插件

Leaflet.ChineseTmsProviders Provider for Chinese Tms Service

  • Leaflet 调用国内各种地图的功能十分复杂,幸好有 leaflet.ChineseTmsProviders 这个插件,这四种地图直接就可以加载进来,十分方便。

  • 使用方法很简单可点击上面链接去 GitHub 看使用说明,或拉这个 demo下来来瞧一瞧代码。

优化 marker 相关的插件

Leaflet 学习资料整理

模块化开发的加载包注意的问题

  • 引 leaflet 包的时候不要忘记引用包里的 css import 'leaflet/dist/leaflet.css';

关于 Leaflet 和 esri-leaflet 一起使用 L.esri.TiledMapLayer 加载 ArcGIS 服务切片底图时,控制台打印报错 Uncaught ReferenceError: proj4 is not defined

  • 查看了下源码 if (!proj4) { warn('L.esri.TiledMapLayer is using a non-mercator spatial reference. Support may be available through Proj4Leaflet http://esri.github.io/esri-leaflet/examples/non-mercator-projection.html');} 问题就出在这里,esri-leaflet 里的一个插件 proj4leaflet 依赖 proj4,所以需要手动引入 proj4 这个包。
  • 这个 GitHub 上面的提问及回答 Github esri-leaflet Issues,原因是 leaflet 不支持该服务坐标系,需要依赖 proj4 进行坐标投影。
  • 如果你是模块化开发,需要再npm i proj4 然后再引入进来好了 import * as proj4 from 'proj4'; window['proj4'] = proj4;
  • 如果你是常规开发,直接添加一个 script 标签引用 CDN 资源上托管的 Proj4js 就是了 <script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.4.4/proj4-src.js"></script>

参考

本文 DEMO 地址: https://github.com/lvisei/leaflet-demo

原文首发地址 https://github.com/lvisei/blog

leaflet-demo's People

Contributors

lvisei avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.