美文网首页我爱编程Leaflet
入门Leaflet之小Demo

入门Leaflet之小Demo

作者: 六小六的世界 | 来源:发表于2018-02-26 00:05 被阅读0次

    入门 Leaflet 之小 Demo


    写在前面 ---- WebGIS 开发基础之 Leaflet

    1. GIS 基本概念:GIS、Map、Layer、Feature、Geometry、Symbol、Data(Point、Polyline、Polygon)、Renderer、Scale、Project、Coordinates;
    2. GIS 开发概述:架构模式、常用平台和 SDK、二维三维
    3. 使用 Leaflet 开发常用功能
    • 地图加载(底图类型、切换):
    • 地图操作(缩放、平移、定位/书签、动画):
    • 图层管理(加载、移除、调整顺序):
    • 要素标绘(点/聚簇、线、面,符号化/静态动态):
    • 属性标注(字段可选、样式定制):
    • 专题地图(点、线、面,渲染):
    • 查询定位(属性查询、空间查询/周边搜索/缓冲区/面查点线面/点线查面、图属互查、综合查询):
    • 信息窗口(入口、Popup、定制):
    • 坐标转换(地理与投影、不同地理坐标系):
    • 空间运算(长度面积测量、点取坐标、缓冲区、相交包含关系):
    • 动态监控(固定点状态切换、车辆监控):


      image
    1. Leaflet API image

    Demo 用到的库


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

    image
    • 库引用
    <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>
    
    • 地图加载与切换
    const map = L.map("mapDiv", {
            crs: L.CRS.EPSG3857, //要使用的坐标参考系统,默认的坐标参考系,互联网地图主流坐标系
            // crs: L.CRS.EPSG4326, //WGS 84坐标系,GPS默认坐标系
            zoomControl: true,
            // minZoom: 1,
            attributionControl: true,
        }).setView([30.6268660000, 104.1528940000], 18);//定位在成都北纬N30°37′45.58″ 东经E104°09′1.44″
        let Baselayer = L.tileLayer(urlTemplate.mapbox_Image, {
           maxZoom: 17, //最大视图
            minZoom: 2, //最小视图
            attribution: 'liuvigongzuoshi@foxmail.com  &copy; <a href="https://github.com/liuvigongzuoshi/WebGIS-for-learnning/tree/master/Leaflet_Demo">WebGIS-for-learnning</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: 2
            }).addTo(map);
            console.log(Baselayer)
        }
    }
    

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

    image
    • 库引用 如上 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>
    
    • 判断浏览器是否支持
        let map;
        let Baselayer;
        // 使用H5 API定位 定位在当前位置
        if (navigator.geolocation) {
            console.log('/* 地理位置服务可用 */')
            navigator.geolocation.getCurrentPosition(h5ApiSuccess, h5ApiError);
        } else {
            console.log('/* 地理位置服务不可用 */')
            mapInit([30.374558, 104.09144]);//指定一个数据 定位在成都北纬N30°37′45.58″ 东经E104°09′1.44″
        }
    
    • 定位成功或失败
        const h5ApiSuccess = (position) => {
            let latitude = position.coords.latitude; //纬度
            let longitude = position.coords.longitude; //经度
            console.log('你的经度纬度分别为' + longitude + ',' + latitude + '。')
            return mapInit([latitude, longitude]);
        };
    
        const h5ApiError = () => {
            console.log('/* 地理位置请求失败 */')
            mapInit([30.374558, 104.09144]);//指定一个数据 定位在成都北纬N30°37′45.58″ 东经E104°09′1.44″
        };
    
    • 成功后初始化底图
        const mapInit = (LatLng) => {
            map = L.map("mapDiv", {
                crs: L.CRS.EPSG3857, //要使用的坐标参考系统,默认的坐标参考系
                // crs: L.CRS.EPSG4326, //国内的坐标参考系
                zoomControl: true,
                // minZoom: 1,
                attributionControl: true,
            }).setView(LatLng, 18);//定位在当前位置
            Baselayer = L.tileLayer(urlTemplate.mapbox_Image, {
                maxZoom: 17, //最大视图
                minZoom: 2, //最小视图
                attribution: 'liuvigongzuoshi@foxmail.com  &copy; <a href="https://github.com/liuvigongzuoshi/WebGIS-for-learnning/tree/master/Leaflet_Demo">WebGIS-for-learnning</a>'
            }).addTo(map);
    
            L.marker(LatLng, {
                highlight: "permanent" //永久高亮显示
            }).addTo(map);
        }
    
    • 更多了解 geolocation 对象,可参考MDN Web 文档
    • 更多了解使用 marker 高亮显示,可参考leaflet.marker.highlight插件
    • 基于 Demo 1 利用 leaflet 封装好的 H5 定位 API,定位到当前位置 Demo

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

    image
    • 库引用 如上 Demo 1

    • 设置地图缩放到指定图层

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

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

    • 地图飞到边界的合适的位置
    map.flyToBounds(polygon.getBounds());   //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);  //地图上绘制一个多形
    
    • 地图定位到边界的合适的位置
    map.fitBounds(polygon.getBounds());  //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

    image
    • 库引用
    <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="./lib/esri-leaflet-v2.1.2/dist/esri-leaflet.js"></script> <!-- esri-leafleat插件 -->
    <script src="./js/urlTemplate.js"></script>
    
    • 使用 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_CQMap_IMG_MAPSERVER_PATH,
            maxZoom: 17,
            minZoom: 0,
            useCors: false, //是否浏览器在跨域的情况下使用GET请求。
        }).addTo(oMap)); //加载第一个底图
    
        oLayer.push(L.esri.tiledMapLayer({
            url: urlTemplate.SYS_CQMap_IMG_LABEL_MAPSERVER_PATH,
            maxZoom: 17,
            minZoom: 0,
            useCors: false,
        }).addTo(oMap));  //加载第二个底图
    
    • 切换底图(移除及加载)
    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,
                    useCors: false,
                    maxZoom: maxZoom, // 设置最大放大图层值
                }).addTo(oMap));
            })
        }
    

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

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

    image
    • 库引用 如上 Demo 1
    • 画一个圆
    // 画一个circle
    const circle = L.circle([36.52, 120.31], {
      color: 'green', //描边色
      fillColor: '#f03',  //填充色
      fillOpacity: 0.5, //透明度
      radius: 10000 //半径,单位米
    }).addTo(map);
    // 绑定一个提示标签
    circle.bindTooltip('我是个圆');
    
    • 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',
      iconSize: [300, 79], // size of the icon
      popupAnchor: [0, -10] // point from which the popup should open relative to the iconAnchor
    });
    
    const oMarker = L.marker([36.52, 124.31], { icon: greenIcon }).addTo(map);
    // 绑定一个提示标签
    oMarker.bindTooltip('这是个自定义Marker', { direction: 'left', offset: [-150, 0] });
    
    • 画一根线
    //画一根线
    const polyline = L.polyline([[45.51, -122.68], [37.77, -122.43], [34.04, -118.2]], { color: 'red' }).addTo(map);
    // 飞到这个线的位置
    // map.fitBounds(polyline.getBounds());
    
    • 画一个多边形
    // 画一个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

    image
    • 库引用 如上 Demo 1
    • 画一个 circle 并绑定一个 Popup
    // 画一个circle
    const circle = L.circle([36.92, 121.31], {
     color: 'green', //描边色
     fillColor: '#f03',  //填充色
     fillOpacity: 0.5, //透明度
     radius: 10000 //半径,单位米
    }).addTo(map);
    
    // 绑定一个弹窗
    circle.bindPopup('我是个圆');
    
    • 定位一个 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)
    });
    
    • 实现动态改变 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

    image
    • 库引用 如上 Demo 1

    • 获得 geojson 并处理数据

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

    模拟后台返回的数据geojson

    • 绘制边界并添加图例
    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);
    }
    
    // 添加图例
    legend.onAdd = legendHtml;
    legend.addTo(oMap);
    
    // 定位到该界限的中心位置
    oMap.flyToBounds(PolygonsCenter);
    }
    
    • 返回边界的填充色及图列的样式
    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 (let 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;
        };
    
    • 鼠标移动上去的事件、鼠标移出的事件、发生点击的事件
    const highlight = (e) => {
        let layer = e.target;
        layer.setStyle({
            weight: 6,
            color: '#fff',
            fillOpacity: 0.9,
            dashArray: '0'
        })
    }
    
    const resetHighlight = (e) => {
        let 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 包的时候不要忘记引用包里的 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
    • 如果你是模块化开发,需要再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>

    Leaflet 学习资料整理

    参考

    持续更新中 原文地址: https://juejin.im/post/5a658614f265da3e3f4cce0e

    相关文章

      网友评论

        本文标题:入门Leaflet之小Demo

        本文链接:https://www.haomeiwen.com/subject/yenbzxtx.html