关于leaflet:leaflet底图样式修改及热力图加载地图打点

<!DOCTYPE html><html><head><meta charset="UTF-8"><title data-i18n="resources.title_heatMap"></title><!-- <script type="text/javascript" include="randomcolor" src="./js/include-web.js"></script> --> <style> #heatNumbers, #heatRadius { width: 50px; display: inline-block; } </style> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.css" /> <link rel="stylesheet" href="https://iclient.supermap.io/dist/leaflet/iclient-leaflet.min.css" /> <link rel="stylesheet" href="./css/MarkerCluster.Default.css" /> <link rel="stylesheet" href="./css/MarkerCluster.css" /> <script type="text/javascript" src="./js/jquery.min.js"> </script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.js"></script> <script type="text/javascript" src="https://iclient.supermap.io/dist/leaflet/iclient-leaflet.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/heatmap.js/2.0.2/heatmap.js"></script> <!-- 应用new HeatmapOverlay --> <script src="./js/leaflet-heatmap.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.heat/0.2.0/leaflet-heat.js"></script> <!--引入proj4,百度地图用--> <script src="./proj4/proj4.js"></script> <script src="./proj4/proj4leaflet.min.js"></script> <!-- 加载高德地图 --> <script src="./js/leaflet.ChineseTmsProviders.js"></script> <!-- 互联网地图纠偏插件 需配合 leaflet.ChineseTmsProviders.js 插件应用 无需js代码,援用后主动纠偏 纠偏后的坐标为WGS84 --> <script src="./js/leaflet.mapCorrection.min.js"></script> <!-- 点聚合 --> <script src="./js/leaflet.markercluster-src.js"></script><script src="./js/data.js"></script><!-- 批改地图款式 --><script src="./js/leaflet-tilelayer-colorizr.js"></script><style> .leaflet-zoom-animated img { -webkit-filter: invert(50%) grayscale(0.5) saturate(0.5) brightness(1.6) opacity(1) hue-rotate(334deg) sepia(10%) !important; -ms-filter: invert(1) grayscale(0.5) saturate(0.5) brightness(1.6) opacity(1) hue-rotate(334deg) sepia(10%) !important; -moz-filter: invert(1) grayscale(0.5) saturate(0.5) brightness(1.6) opacity(1) hue-rotate(334deg) sepia(10%) !important; filter: invert(1) grayscale(0.5) saturate(0.5) brightness(1.6) opacity(1) hue-rotate(334deg) sepia(1%) !important; }</style></head><body style=" margin: 0;overflow: hidden;background: #fff;width: 100%;height:100%;position: absolute;top: 0;"><div id="map" style="margin:0 auto;width: 100%;height: 100%"></div><script type="text/javascript"> var map, resultLayer,normal,image,baseLayers; var mapMarkers = []; var markers; addGaodeLayer() map = L.map('map', { preferCanvas: true, center: [34.342329669, 109.000431666], layers: [normal], maxZoom: 20, zoom: 12 }); // http://192.168.0.105:9090/img/{z}/{x}/{y}.png // 这个是瓦片地图的地址 L.tileLayer.colorizr("http://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}", { maxZoom: 18, minZoom: 3, colorize: function (pixel) { // 这个办法用来调整所有的图片上的rgb值,pixel是图片原有的rgb值 pixel.r += 13; pixel.g += 97; pixel.b += 190; return pixel; } }).addTo(map); // L.control.layers(baseLayers, null).addTo(map); loadHeatMap(); function getIcon(type){ var defaultIcon = L.icon({ iconUrl: '../src/assets/IC/maker1_gif.webp', iconSize: [40, 40], // 图标的大小 【值1,值2】 为具体你自定义图标的尺寸,比方我图标尺寸是32×52,示意该图标:宽度32像素,高度:52像素,那么值1:就是32,值2:就是52 iconAnchor: [20, 20], // 图标将对应标记点的地位 这个是重点, 【值1,值2】,值1:为图标坐标第一个值(即32)的一半,值2:为图标坐标第二个值(即52) }); return defaultIcon } function loadHeatMap() { var cfg = { max: 50, radius: 14, // scaleRadius: true, //设置热力点是否平滑过渡 // useLocalExtrema: true, //应用部分极值 blur: 0.95, //系数越高,突变越平滑,默认是0.85 latField: 'lat', lngField: 'lng', //经度 valueField: 'count', //热力点的值 minOpacity: 0.5, gradient: { 0.5: 'blue', // [0,0.9) 0.925: 'skyblue', // [0.9,0.93) 0.99: 'green', 0.9950: '#ffea00', 0.995551: 'orange', 1.0: 'red' } } var heatPoints = makerData resultLayer = new HeatmapOverlay(cfg); console.log('heatPoints==', heatPoints) // resultLayer = L.heatLayer(heatPoints, cfg).addTo(map); resultLayer.setData({ max:50, data: heatPoints }); map.addLayer(resultLayer); // 打点测试 setMakers(makerData) } function addGaodeLayer(){ var normalm = L.tileLayer.chinaProvider('GaoDe.Normal.Map', { maxZoom: 18, minZoom: 5 }); var imgm = L.tileLayer.chinaProvider('GaoDe.Satellite.Map', { maxZoom: 18, minZoom: 5 }); var imga = L.tileLayer.chinaProvider('GaoDe.Satellite.Annotion', { maxZoom: 18, minZoom: 5 }); normal = L.layerGroup([normalm]), image = L.layerGroup([imgm, imga]); baseLayers = { "地图": normal, "影像": image, } } function setMakers(list){ markers = L.markerClusterGroup(); // 标记点位组 for (var i = 0; i < list.length; i++) { var currentItem = list[i]; var title = currentItem.count; var marker = L.marker(new L.LatLng(currentItem.lat, currentItem.lng), { title: '详情', icon :getIcon() }); let showItem = [ { key: '经度', val: currentItem.lng }, { key: '纬度', val: currentItem.lat } ] let basicInfo = '' for (let i = 0; i < showItem.length; i++) { basicInfo += '<div class="info_row"><span class="info_lable">' + showItem[i].key + ':</span>' + showItem[i].val + '</div>' } let myTitle = '<div class="info_title">点位详情</div>' window.basicDom = '<div class="beauty-scroll">' + myTitle + basicInfo + '<div id="map_video_box" class="video_play_block">' + `<div id="play_video_block" style="width:100%;height:99%;"></div>` + '</div>' + '</div>' marker.bindPopup(basicDom); markers.addLayer(marker); } // map.addLayer(markers); // for (var i = 0; i < list.length; i++) { // var a = list[i]; // var data = a // var marker = L.marker(new L.LatLng(a.lat, a.lng), {title: '' , icon :getIcon(a.index)}); // window.currentData = data // marker.bindPopup(basicDom); // 放弃所有弹出窗口关上 // markers.addLayer(marker); // // mapMarkers.push(marker) // } map.addLayer(markers); }</script></body></html>

October 27, 2022 · 3 min · jiezi

关于leaflet:基于leaflet封装的框选四至多边形框选的class类

import L from 'leaflet';import 'leaflet-draw'/*** 案例* async ceju(){* let that = this* const p1 = await new Promise(function(resolve,reject){* let a = new Draw(that.baseMap,that.measureGroup,resolve,reject)* a.rectangle()* }); * console.log(p1);* * }, ***///new的时候须要传入map以及承接的layergroup//返回值的时候须要用异步去承接,所以采纳了promiseclass Draw{ constructor(map,measureGroup,resolve,reject){ this.map = map this.drawDataMap = [] this.stopRectArea = null this.locations = {} this.resolve = resolve this.reject = reject this.measureGroup = measureGroup this.geometry = [] } rectangle(){ let that = this console.log('开始框选'); that.map.off("click") that.map.off("dblclick") that.map.off("dblclick") if(that.stopRectArea != null){ //stopRectArea在data中定义,革除反复的拉框操作 that.map.off('mousedown', that.stopRectArea.mousedown); } var rectangleMeasure = { startPoint: null, endPoint: null, rectangle:null, layer: that.measureGroup, color: "rgba(51,136,255,1)", addRectangle:function(){ rectangleMeasure.destory(); var bounds = []; bounds.push(rectangleMeasure.startPoint); bounds.push(rectangleMeasure.endPoint); rectangleMeasure.rectangle = L.rectangle(bounds, {color: rectangleMeasure.color, weight: 1}); rectangleMeasure.rectangle.addTo(rectangleMeasure.layer); rectangleMeasure.layer.addTo(that.map); }, mousedown: function(e){ rectangleMeasure.rectangle = null; that.map.dragging.disable(); rectangleMeasure.startPoint = e.latlng; that.map.on('mousemove',rectangleMeasure.mousemove) }, mousemove:function(e){ rectangleMeasure.endPoint = e.latlng; rectangleMeasure.addRectangle(); that.map.off('mousedown ', rectangleMeasure.mousedown).on('mouseup', rectangleMeasure.mouseup); }, mouseup: function(){ that.map.dragging.enable(); that.map.off('mousemove',rectangleMeasure.mousemove).off('mouseup', rectangleMeasure.mouseup); that.locations = {}; //locations在data中定义 that.locations['leftX'] = rectangleMeasure.startPoint.lat; that.locations['leftY'] = rectangleMeasure.startPoint.lng; that.locations['rightX'] = rectangleMeasure.endPoint.lat; that.locations['rightY'] = rectangleMeasure.endPoint.lng; that.locations['layer_id'] = rectangleMeasure.layer._leaflet_id; that.locations['layer'] = rectangleMeasure.layer; that.locations['rectangle'] = rectangleMeasure.rectangle; that.drawDataMap.push([rectangleMeasure.startPoint.lat,rectangleMeasure.startPoint.lng]) that.drawDataMap.push([rectangleMeasure.endPoint.lat,rectangleMeasure.startPoint.lng]) that.drawDataMap.push([rectangleMeasure.endPoint.lat,rectangleMeasure.endPoint.lng]) that.drawDataMap.push([rectangleMeasure.startPoint.lat,rectangleMeasure.endPoint.lng]) that.resolve(that.drawDataMap) }, destory:function(){ if(rectangleMeasure.rectangle) rectangleMeasure.layer.removeLayer(rectangleMeasure.rectangle); } }; that.stopRectArea = rectangleMeasure; //记录已点击拉框按钮,未在地图上拉框 that.map.on('mousedown', rectangleMeasure.mousedown); //在地图上拉框 } polygon(){ let that = this that.measureGroup.clearLayers() var points,lines,tempLines,node; that.map.off("mousemove") that.map.off("mousedown") that.map.off("mouseup") function drawPolygon(){ that.map.doubleClickZoom.disable(); lines = new L.polyline([]); tempLines = new L.polyline([],{ dashArray: 5 }); points = []; that.geometry = []; that.map.on('click', onClick); //点击地图 that.map.on('dblclick', onDoubleClick); that.map.on('mousemove', onMove)//双击地图 function onClick(e) { points.push([e.latlng.lat, e.latlng.lng]) lines.addLatLng(e.latlng) that.measureGroup.addLayer(tempLines) that.measureGroup.addLayer(lines) node=L.circle(e.latlng, { color: '#ff0000', fillColor: 'ff0000', fillOpacity: 1 }) that.measureGroup.addLayer(node) that.geometry.push(node) } function onMove(e) { if (points.length > 0) { let ls = [points[points.length - 1], [e.latlng.lat, e.latlng.lng], points[0]] tempLines.setLatLngs(ls) // that.map.addLayer(tempLines) } } function onDoubleClick() { that.geometry.push(L.polygon(points).addTo(that.map)) that.drawDataMap = points that.drawDataMap.splice(that.drawDataMap.length-1,1) points = []; node=null; that.map.off('click', onClick); //点击地图 that.map.off('dblclick', onDoubleClick); that.map.off('mousemove', onMove)//双击地图 that.map.doubleClickZoom.enable(); that.resolve(that.drawDataMap) } } that.removePolygon() drawPolygon() } removePolygon(){ for(let ooo of this.geometry){ ooo.remove(); } }}export default Draw

February 17, 2022 · 2 min · jiezi

关于leaflet:基于leaflet封装测距测面class类

import L from 'leaflet';import 'leaflet-draw'class DrawPlug { constructor(map,measureGroup) { //测距侧面 this.DRAWING = false; //是否正在绘制 this.DRAWLAYERS = []; this.BarDRAWLAYERS = []; this.ISMEASURE = true; //是否是量距 this.MEASURETOOLTIP; //量距提醒 this.MEASUREAREATOOLTIP; //量面提醒 this.MEASURERESULT = 0; //测量后果 this.DRAWPOLYLINE; //绘制的折线 this.DRAWMOVEPOLYLINE; //绘制过程中的折线 this.DRAWPOLYLINEPOINTS = []; //绘制的折线的节点集 this.DRAWPOLYGON; //绘制的面 this.DRAWMOVEPOLYGON; //绘制过程中的面 this.DRAWPOLYGONPOINTS = []; //绘制的面的节点集 this.map = map; this.measureGroup = measureGroup; } startDrawLine() { let that = this that.MEASURERESULT = 0; //测量后果 that.map.getContainer().style.cursor = 'crosshair'; var shapeOptions = { color: '#3388ff', weight: 3, opacity: 0.8, fill: false, clickable: true } that.DRAWPOLYLINE = new L.Polyline([], shapeOptions); //绘制的折线 that.measureGroup.addLayer(that.DRAWPOLYLINE); if (that.ISMEASURE) { //是否是量距 that.MEASURETOOLTIP = new L.Tooltip(that.map); //量距提醒 } that.map.on('mousedown', onClick); that.map.on('dblclick', onDoubleClick); function onClick(e) { console.log('onClick'); that.DRAWING = true; //是否正在绘制 that.DRAWPOLYLINEPOINTS.push(e.latlng); //绘制的折线的节点集 if (that.DRAWPOLYLINEPOINTS.length > 1 && that.ISMEASURE) { //是否是量距 that.MEASURERESULT += e.latlng.distanceTo(that.DRAWPOLYLINEPOINTS[that.DRAWPOLYLINEPOINTS.length - 2]); } that.DRAWPOLYLINE.addLatLng(e.latlng); //绘制的折线 that.map.on('mousemove', onMove); } function onMove(e) { if (that.DRAWING) { //是否正在绘制 if (that.DRAWMOVEPOLYLINE != undefined && that.DRAWMOVEPOLYLINE != null) { //绘制过程中的折线 that.measureGroup.removeLayer(that.DRAWMOVEPOLYLINE); } var prevPoint = that.DRAWPOLYLINEPOINTS[that.DRAWPOLYLINEPOINTS.length - 1]; that.DRAWMOVEPOLYLINE = new L.Polyline([prevPoint, e.latlng], shapeOptions); that.measureGroup.addLayer(that.DRAWMOVEPOLYLINE); if (that.ISMEASURE) { var distance = that.MEASURERESULT + e.latlng.distanceTo(that.DRAWPOLYLINEPOINTS[that.DRAWPOLYLINEPOINTS.length - 1]); console.log(distance); } } } function onDoubleClick(e) { that.map.getContainer().style.cursor = ''; /*显示两点间隔*/ var distance = that.MEASURERESULT + e.latlng.distanceTo(that.DRAWPOLYLINEPOINTS[that.DRAWPOLYLINEPOINTS.length - 1]); var myIcon = L.divIcon({ // html: (distance / 1000).toFixed(2) + "公里", className: 'my-div-icon', html:"<div>"+"<p>"+(distance / 1000).toFixed(2)+"KM</p>"+"</div>", iconSize:26 }); for(let i=0;i<that.DRAWPOLYLINEPOINTS.length-2;i++){ let distance_cust = that.DRAWPOLYLINEPOINTS[i].distanceTo(that.DRAWPOLYLINEPOINTS[i+1]); let myIcon_cust = L.divIcon({ // html: (distance / 1000).toFixed(2) + "公里", className: 'my-div-icon', html:"<div>"+"<p>"+(distance_cust / 1000).toFixed(2)+"KM</p>"+"</div>", iconSize:26 }); let site_c = L.polyline([that.DRAWPOLYLINEPOINTS[i],that.DRAWPOLYLINEPOINTS[i+1]],{color:'rgba(0,0,0,0)',}).addTo(that.measureGroup); let as = site_c.getCenter() let c = new L.Marker(as, { draggable: false,icon: myIcon_cust}); that.measureGroup.addLayer(c) } let marker = new L.Marker(e.latlng, { draggable: false,icon: myIcon}); that.measureGroup.addLayer(marker) if (that.DRAWING) { if (that.DRAWMOVEPOLYLINE != undefined && that.DRAWMOVEPOLYLINE != null) { that.map.removeLayer(that.DRAWMOVEPOLYLINE); that.DRAWMOVEPOLYLINE = null; } that.BarDRAWLAYERS.push(that.DRAWPOLYLINE); that.DRAWPOLYLINEPOINTS = []; that.DRAWING = false; that.ISMEASURE = false; that.map.off('mousedown'); that.map.off('mousemove'); that.map.off('dblclick'); } } } clearLayer(){ let that = this that.measureGroup.clearLayers() } startDrawPolygon() { let that = this that.MEASURERESULT = 0; that.map.getContainer().style.cursor = 'crosshair'; that.map.on('mousedown', function(e) { that.DRAWING = true; that.DRAWPOLYGONPOINTS.push(e.latlng); that.DRAWPOLYGON.addLatLng(e.latlng); }); that.map.on('mousemove', function(e) { if (that.DRAWING) { if (that.DRAWMOVEPOLYGON != undefined && that.DRAWMOVEPOLYGON != null) { that.map.removeLayer(that.DRAWMOVEPOLYGON); } var prevPoint = that.DRAWPOLYGONPOINTS[that.DRAWPOLYGONPOINTS.length - 1]; var firstPoint = that.DRAWPOLYGONPOINTS[0]; that.DRAWMOVEPOLYGON = new L.Polygon([firstPoint, prevPoint, e.latlng], shapeOptions); that.measureGroup.addLayer(that.DRAWMOVEPOLYGON); } }); that.map.on('dblclick', function(e) { that.map.getContainer().style.cursor = ''; var tempPoints = []; for (var i = 0; i < that.DRAWPOLYGONPOINTS.length; i++) { tempPoints.push(that.DRAWPOLYGONPOINTS[i]); } let as = that.DRAWPOLYGON.getLatLngs()[0] as.pop() let last = as[0] as.push(last) for(let i=0;i<as.length-1;i++){ let distance_cust = as[i].distanceTo(as[i+1]); let myIcon_cust = L.divIcon({ // html: (distance / 1000).toFixed(2) + "公里", className: 'my-div-icon', html:"<div>"+"<p>"+(distance_cust / 1000).toFixed(2)+"KM</p>"+"</div>", iconSize:26 }); let site_c = L.polyline([as[i],as[i+1]],{color:'rgba(0,0,0,0)',}).addTo(that.measureGroup); let as_ccc = site_c.getCenter() let c = new L.Marker(as_ccc, { draggable: false,icon: myIcon_cust}); that.measureGroup.addLayer(c) } console.log(as); tempPoints.push(e.latlng); let CC = that.DRAWPOLYGON.getCenter() var distance = CalArea(tempPoints); var myIcon = L.divIcon({ html:"<div>"+"<p>"+(distance / 1000000).toFixed(3)+"KM²</p>"+"</div>", className: 'my-div-icon', iconSize:26 }); let marker = new L.Marker(CC, { draggable: false,icon:myIcon }); that.measureGroup.addLayer(marker); // marker.bindPopup("总面积:" + (distance / 1000000).toFixed(3) + '平方公里').openPopup(); if (that.DRAWING) { if (that.DRAWMOVEPOLYGON != undefined && that.DRAWMOVEPOLYGON != null) { that.map.removeLayer(that.DRAWMOVEPOLYGON); that.DRAWMOVEPOLYGON = null; } that.BarDRAWLAYERS.push(that.DRAWPOLYGON); that.DRAWPOLYGONPOINTS = []; that.DRAWING = false; that.ISMEASURE = false; that.map.off('mousedown'); that.map.off('mousemove'); that.map.off('dblclick'); } }); var shapeOptions = { color: '#3388ff', weight: 3, opacity: 0.8, fill: true, fillColor: null, fillOpacity: 0.2, clickable: true } that.DRAWPOLYGON = new L.Polygon([], shapeOptions); that.measureGroup.addLayer(that.DRAWPOLYGON); if (that.ISMEASURE) { that.MEASUREAREATOOLTIP = new L.Tooltip(that.map); } function CalArea(latLngs) { var pointsCount = latLngs.length, area = 0.0, d2r = Math.PI / 180, p1, p2; if (pointsCount > 2) { for (var i = 0; i < pointsCount; i++) { p1 = latLngs[i]; p2 = latLngs[(i + 1) % pointsCount]; area += ((p2.lng - p1.lng) * d2r) * (2 + Math.sin(p1.lat * d2r) + Math.sin(p2.lat * d2r)); } area = area * 6378137.0 * 6378137.0 / 2.0; } return Math.abs(area); } }}export default DrawPlug

February 17, 2022 · 3 min · jiezi

关于leaflet:leaflet加载本地shapefile的zip包

业务场景:通过el-upload上传shapefile的zip文件,而后在地图上加载采纳了shp.js包 <div class="upload-file"> <el-upload class="upload-demo" drag :http-request="UploadShp" :before-upload="onBeforeUploadImage" action="string" multiple> <i class="el-icon-upload"></i> <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> <div class="el-upload__tip" slot="tip">需.zip文件模式,至多包含.dbf、.shp、.prj三类文件</div> </el-upload></div>原作者:地址:https://segmentfault.com/u/yo...这个shp.js包巨坑,Readme说是间接传zip的url或者间接本地读取到的zip间接传入就能够,然而把他源码拆开看须要传arrayBuffer格局的,而且只能传这种格局。心愿这个包的作者看到这篇文章的时候去改一下报错提醒,或者改一下备注!!!!!太坑了 onBeforeUploadImage(file){ this.fileToBuf(file)},fileToBuf(file){ let that = this var fr = new FileReader(); fr.readAsArrayBuffer(file); fr.addEventListener("loadend",(e) => { var buf = e.target.result; shp(buf).then(function(data){ data.features.forEach(element => { var latlngs = element.geometry.coordinates let a = [] latlngs[0].forEach(element => { a.push([element[1],element[0]]) }); //supermap idesktop生成的shepefile的经纬度对于leaflet来说是反的,所以这里调整了一下 L.polygon(a, {color: 'red',fillColor:'rgba(255,0,0,0.1)'}).addTo(that.map); }); }); },false);},

December 27, 2021 · 1 min · jiezi

关于leaflet:支持Canvas的LeafletPathDashFlow动态流向线

通过对leaflet以及其插件的学习,咱们理解到应用Leaflet.Path.DashFlow插件可实现轨迹动静展现、管道流向动静展现、河流流向动静展现等,达到加强可视化展现的成果。该插件应用形式非常简单,只需在失常增加线的时候,退出dashArray和dashSpeed参数即可。外围代码如下: 留神,在dashSpeed为负时,线是正向流动。成果如下: 然而在应用的过程中,发现该插件有个弊病,就是当应用Canvas形式绘制地图(初始化地图preferCanvas参数为true)时,动态效果不可用。那要如何解决这个问题呢? 通过对Leaflet.Path.DashFlow以及leaflet源码的钻研,发现动静线的成果次要是通过动静刷新dashOffset参数的值,以扭转线的款式来实现。 L.SVG在_updateStyle的时候,更新了dashOffset参数,然而L.Canvas在_updateStyle时,并没有更新dashOffset参数。这即是在Canvas形式绘制时没有动态效果的起因。 L.SVG: L.Canvas: 由此,咱们找到了解决思路,即在L.Canvas的_updateStyle办法中,参考L.SVG的解决形式,增加对dashOffset参数的管制。外围代码如下: 为方便使用,咱们将L.Path.DashFlow插件从新封装,大家援用这个插件,即可在Canvas和SVG两种形式下应用此插件。 总结应用Leaflet.Path.DashFlow插件可实现轨迹动静展现、管道流向动静展现等动静线成果。默认插件在应用Canvas形式绘制地图(初始化地图preferCanvas参数为true)时,动态效果不可用。通过批改L.Canvas中代码,即可在Canvas形式时实现动静线成果。将L.Path.DashFlow插件从新封装,援用插件,即可在Canvas和SVG两种形式下实现动静线成果。在线示例[在线示例](http://gisarmory.xyz/blog/ind... [残缺代码](http://gisarmory.xyz/blog/ind... 原文地址:http://gisarmory.xyz/blog/index.html?blog=LeafletPathDashFlow。 关注《GIS兵器库》公众号, 第一工夫取得更多高质量GIS文章。 本文章采纳 常识共享署名-非商业性应用-雷同形式共享 4.0 国内许可协定 进行许可。欢送转载、应用、从新公布,但务必保留文章署名《GIS兵器库》(蕴含链接:  http://gisarmory.xyz/blog/),不得用于商业目标,基于本文批改后的作品务必以雷同的许可公布。

November 10, 2020 · 1 min · jiezi

Leaflet-Develop-Guide ????

Leaflet-Develop-Guide ????文档Leaflet 官方文档,Leaflet 插件集。Leaflet 中文文档,中文插件集。火星科技翻译,文档所使用的Leaflet版本是 v1.0.3。Leaflet 常用 API 结构图。Leaflet 类之间继承关系图。常用插件绘制编辑插件矢量绘图 Leaflet Draw , API 参考文档,See the demo。几何图形编辑 Leaflet Editable, API 参考文档,See the demo。地图服务资源加载插件基于leaflet加载ArcGIS服务插件 esri-leaflet。国内常用地图服务资源加载插件Leaflet.ChineseTmsProviders Provider for Chinese Tms Service,方便快捷的调用地图服务。支持的地图服务类型:TianDiTu、GaoDe、Google、Geoq、OSM。优化 marker 相关的插件提供了丰富多彩的图标 Leaflet.awesome-markers, See the demo map。强大的集聚插件 Leaflet.markercluster, See the demo map。优化的label Leaflet.label, See the demo map。优化重叠在一起的markers OverlappingMarkerSpiderfier-Leaflet, See the demo map。优化在边框上显示不在当前视野中的marker Leaflet.EdgeMarker, See the demo map。聚合插件轻量简单的热力图插件 Leaflet heat,See the demo。地理空间分析一个可运行在浏览器和 Node.js中的高级地理空间分析库 Turf.js。框架化Vue 2 components for Leaflet maps Vue2Leaflet,使用文档,See the doc。React components for Leaflet maps react-leaflet,使用文档,See the doc。持续更新中 原文地址: https://juejin.im/post/5c6fad… ...

February 28, 2019 · 1 min · jiezi

百度地图 osm地图 leaflet echarts webapck的组合使用时的踩坑记录

webpack+百度地图创建 script标签进行加载export function MP(ak){ return new Promise(function (resolve, reject){ window.onload = function () { resolve(BMap) } var script = document.createElement(“script”); script.type = “text/javascript”; script.src = “http://api.map.baidu.com/api?v=2.0&ak="+ak+"&callback=init"; script.onerror = reject; document.head.appendChild(script); }); }使用:import {MP} from ‘./map.js’; MP(“your ak”).then(BMap => { // do something})webpack+百度地图+echart需要1 百度地图2 echart3 bmap.min.js 添加扩展,用于让百度地图支持echart https://github.com/apache/inc...webpack+osm地图+leaflet可能会遇见两个问题:1 地图图片错位 忘记加载leaflet.css2 webpack 中使用leaflet 的一个主要问题是默认图标的加载问题,详见https://segmentfault.com/q/10…另外也可以考虑使用动态创建<script>标签的方法,类似百度地图加载webpack+百度地图+leaflet因为leaflet本身支持的是WGS84的坐标系 ,而百度地图在中国使用的是百度坐标系,所以如果要在百度地图中使用leaflet的话,一是需要绘图数据变更为百度地图的BD09坐标系,二是需要对leaflet添加扩展,使其在进行经纬度坐标转化时使用百度地图的映射系统解决方案: http://tiven.wang/articles/us…以上问题的代码示例 https://gitlab.com/dahou/maps

February 27, 2019 · 1 min · jiezi

[原] 解密 Uber 数据团队的大规模地理数据可视化神器:Deck.gl 与 H3

背景如何大规模可视化地理数据一直都是一个业界的难点,随着2015年起 Uber 在这一领域的发力,构建了基于 Deck.gl + H3 (deckgl,h3r) 的大规模数据可视化方案。一方面,极大地满足了大规模地理数据可视化的需求。另一方面,也极大地方便了数据科学家的可视化工作。在大规模空间轨迹分析、交通流量与供需预测等领域得到广泛应用,突破了原来leaflet架构中数据量(通常不会超过10W个原始点)的瓶颈问题,实现百万点绘制无压力,并且可以结合GPU实现加速渲染。地理单元:H3随着互联网出行公司的全球化扩张,越来越多的公司涌现出对地理单元划分的需求。一方面,传统的地理单元比如 S2和geohash,在不同纬度的地区会出现地理单元单位面积差异较大的情况,这导致业务指标和模型输入的特征存在一定的分布倾斜和偏差,使用六边形地理单元可以减少指标和特征normalization的成本。另一方面,在常用的地理范围查询中,基于矩形的查询方法,存在8邻域到中心网格的距离不相等的问题,也就是说六边形网格与周围网格的距离有且仅有一个,而四边形存在两类距离,而六边形的周围邻居到中心网格的距离却是相等的,从形状上来说更加接近于圆形。所以,基于hexagon的地理单元已经成为各大厂家的首选,比如 Uber 和 Didi 的峰时定价服务。在这样的背景下 Uber 基于六边形网格的地理单元开源解决方案 H3 应运而生,它使得部署 Hexagon 方案的成本非常低,通过UDF、R pacakge等方式可以以非常低的成本大规模推广。H3 的前身其实是 DDGS(Discrete global grid systems) 中的 ISEA3H,其原理是把无限的不规则但体积相等的六棱柱从二十面体中心延伸,这样任何半径的球体都会穿过棱镜形成相等的面积cell,基于该标准使得每一个地理单元的面积大小就可以保证几乎相同。然而原生的 ISEA3H 方案在任意级别中都存在12个五边形,H3 的主要改进是通过坐标系的调整将其中的五边形都转移到水域上,这样就不影响大多数业务的开展。下面是 ISEA3H 五边形问题的示例:#Include librarieslibrary(dggridR)library(dplyr)#Construct a global grid with cells approximately 1000 miles acrossdggs <- dgconstruct(spacing=1000, metric=FALSE, resround=‘down’)#Load included test data setdata(dgquakes)#Get the corresponding grid cells for each earthquake epicenter (lat-long pair)dgquakes$cell <- dgGEO_to_SEQNUM(dggs,dgquakes$lon,dgquakes$lat)$seqnum#Converting SEQNUM to GEO gives the center coordinates of the cellscellcenters <- dgSEQNUM_to_GEO(dggs,dgquakes$cell)#Get the number of earthquakes in each cellquakecounts <- dgquakes %>% group_by(cell) %>% summarise(count=n())#Get the grid cell boundaries for cells which had quakesgrid <- dgcellstogrid(dggs,quakecounts$cell,frame=TRUE,wrapcells=TRUE)#Update the grid cells’ properties to include the number of earthquakes#in each cellgrid <- merge(grid,quakecounts,by.x=“cell”,by.y=“cell”)#Make adjustments so the output is more visually interestinggrid$count <- log(grid$count)cutoff <- quantile(grid$count,0.9)grid <- grid %>% mutate(count=ifelse(count>cutoff,cutoff,count))#Get polygons for each country of the worldcountries <- map_data(“world”)#Plot everything on a flat mapp<- ggplot() + geom_polygon(data=countries, aes(x=long, y=lat, group=group), fill=NA, color=“black”) + geom_polygon(data=grid, aes(x=long, y=lat, group=group, fill=count), alpha=0.4) + geom_path (data=grid, aes(x=long, y=lat, group=group), alpha=0.4, color=“white”) + geom_point (aes(x=cellcenters$lon_deg, y=cellcenters$lat_deg)) + scale_fill_gradient(low=“blue”, high=“red”)p转化坐标系后:#Replot on a spherical projectionp+coord_map(“ortho”, orientation = c(-38.49831, -179.9223, 0))+ xlab(’’)+ylab(’’)+ theme(axis.ticks.x=element_blank())+ theme(axis.ticks.y=element_blank())+ theme(axis.text.x=element_blank())+ theme(axis.text.y=element_blank())+ ggtitle(‘Your data could look like this’)H3 中还提供了类似 S2 的六边形压缩技术,使得数据的存储空间可以极大压缩,在处理大规模稀疏数据时将体现出优势:地理数据可视化:Deck.gl在使用 Deck.gl 之前,业界通用的解决方案通常是另一个开源的轻量级地理数据可视化框架 Leaflet。Leaflet 经过十余年的积累已经拥有足够成熟的生态,支持各式各样的插件扩展。不过随着 Leaflet 也暴露出一些新的问题,比如如何大规模渲染地理数据,支持诸如 轨迹、风向、六边形网格的可视化。好在近年来 Mapbox 和 Deck.gl 正在着手改变这一现状。下面是一个具体的例子,如何可视化Hexagon:# 初始化devtools::install_github(“crazycapivara/deckgl”)library(deckgl)# 设置 Mapbox token,过期需要免费在 Mapbox 官网申请Sys.setenv(MAPBOX_API_TOKEN = “pk.eyJ1IjoidWJlcmRhdGEiLCJhIjoiY2poczJzeGt2MGl1bTNkcm1lcXVqMXRpMyJ9.9o2DrYg8C8UWmprj-tcVpQ”)# 数据集合sample_data <- paste0( “https://raw.githubusercontent.com/", “uber-common/deck.gl-data/”, “master/website/sf-bike-parking.json”)properties <- list( pickable = TRUE, extruded = TRUE, cellSize = 200, elevationScale = 4, getPosition = JS(“data => data.COORDINATES”), getTooltip = JS(“object => object.count”))# 可视化deckgl(zoom = 11, pitch = 45) %>% add_hexagon_layer(data = sample_data, properties = properties) %>% add_mapbox_basemap(style = “mapbox://styles/mapbox/light-v9”) 除了六边形之外 Deck.gl 也支持其他常见几何图形,比如 Grid、Arc、Contour、Polygon 等等。更多信息可以见官方文档: https://crazycapivara.github….地理仪表盘:结合 ShinyDeck.gl 结合 Shiny 后,可将可视化结果输出到仪表盘上:library(mapdeck)library(shiny)library(shinydashboard)library(jsonlite)ui <- dashboardPage( dashboardHeader() , dashboardSidebar() , dashboardBody( mapdeckOutput( outputId = ‘myMap’ ), sliderInput( inputId = “longitudes” , label = “Longitudes” , min = -180 , max = 180 , value = c(-90, 90) ) , verbatimTextOutput( outputId = “observed_click” ) ))server <- function(input, output) { set_token(‘pk.eyJ1IjoidWJlcmRhdGEiLCJhIjoiY2poczJzeGt2MGl1bTNkcm1lcXVqMXRpMyJ9.9o2DrYg8C8UWmprj-tcVpQ’) ## 如果token 过期了,需要去Mapbox官网免费申请一个 origin <- capitals[capitals$country == “Australia”, ] destination <- capitals[capitals$country != “Australia”, ] origin$key <- 1L destination$key <- 1L df <- merge(origin, destination, by = ‘key’, all = T) output$myMap <- renderMapdeck({ mapdeck(style = mapdeck_style(‘dark’)) }) ## plot points & lines according to the selected longitudes df_reactive <- reactive({ if(is.null(input$longitudes)) return(NULL) lons <- input$longitudes return( df[df$lon.y >= lons[1] & df$lon.y <= lons[2], ] ) }) observeEvent({input$longitudes}, { if(is.null(input$longitudes)) return() mapdeck_update(map_id = ‘myMap’) %>% add_scatterplot( data = df_reactive() , lon = “lon.y” , lat = “lat.y” , fill_colour = “country.y” , radius = 100000 , layer_id = “myScatterLayer” ) %>% add_arc( data = df_reactive() , origin = c(“lon.x”, “lat.x”) , destination = c(“lon.y”, “lat.y”) , layer_id = “myArcLayer” , stroke_width = 4 ) }) ## observe clicking on a line and return the text observeEvent(input$myMap_arc_click, { event <- input$myMap_arc_click output$observed_click <- renderText({ jsonlite::prettify( event ) }) })}shinyApp(ui, server)参考资料Uber H3 原理分析http://strimas.com/spatial/he…https://cran.r-project.org/we...https://en.wikipedia.org/wiki...http://www.pyxisinnovation.co...Large Scale Data Visualisation with Deck.gl and Shinyhttps://uber.github.io/h3/https://eng.uber.com/shan-he/https://eng.uber.com/keplergl/[译] 解密 Uber 数据部门的数据可视化最佳实践作为分享主义者(sharism),本人所有互联网发布的图文均遵从CC版权,转载请保留作者信息并注明作者 Harry Zhu 的 FinanceR专栏:https://segmentfault.com/blog…,如果涉及源代码请注明GitHub地址:https://github.com/harryprince。微信号: harryzhustudio商业使用请联系作者。 ...

October 22, 2018 · 3 min · jiezi