当前位置: 首页 > news >正文

源码分析之Leaflet中GeoJSON模块

概述

GeoJSON.js中定义了一个GeoJSON类,该类继承自FeatureGroup,用于将GeoJSON数据解析并显示在地图上。此外还定义了很多静态函数和工具方法,如geometryToLayercoordsToLatLng等。

源码分析

GeoJSON类的源码实现

GeoJSON类的源码实现如下:

export var GeoJSON = FeatureGroup.extend({initialize:function(geojson,options){Util.setOptions(this,options); //合并选项this._layers = {}; // 存储子图层的哈希表,用于快速查找if(geojson){this.addData(geojson); // 若传入geojson数据,就立即解析}},addData:function(geojson){// 统一处理数组,遍历featuresvar features = Util.isArray(geojson) ? geojson : geojson.features,i,len,feature;if(features){for(i=0,len=features.length;i<len;i++){feature=features[i];if(feature.geometries||feature.geometry||feature.features||feature.coordinates){this.addData(feature);}}return this;}var options=this.options;// 过滤不符合条件的要素if(options.filter&&!options.filter(geojson)){return this;   }// 将GeoJSON几何体转换为图层var layer=geometryToLayer(geojson,options);if(!layer){return this;}// 将原始GeoJSON附加到图层的feature属性上layer.feature=asFeature(geojson);// 保留原始样式layer.defaultOptions=layer.options;// 应用options.style样式函数this.resetStyle(layer);if(options.onEachFeature){// 调用options.onEachFeature回调函数,传入要素和图层,用户可自定义逻辑options.onEachFeature(geojson,layer);  }// 将图层添加到FeatureGroupreturn this.addLayer(layer);},resetStyle:function(style){return this.eachLayer(function(layer){ // 还原图层选项到初始状态this._setLayerStyle(layer,style) // 重新应用全局样式函数},this)},_setLayerStyle:function(layer,style){if(layer.setStyle){if(typeof style === 'function'){style=style(layer.feature);} layer.setStyle(style) // 调用setStyle方法设置样式}}
})
GeoJSON类源码解析

1.initialize(geojson,options)

  • 功能:初始化GeoJSON图层
  • 参数
    • geojsonGeoJSON数据,支持FeatureFeatureCollectionGeometryGeometryCollection
    • options:配置选项,如styleonEachFeature
  • 流程
    1. 通过Util.setOptions合并默认选项与用户选项
    2. 初始化_layers对象存储子图层
    3. 若传入geojson,调用addData方法解析数据

2.addData(geojson)

  • 功能​​:解析并添加 GeoJSON 数据到图层。
  • 流程​​:
    1. 判断输入是否为 FeatureCollection(检查 features 属性),遍历每个 Feature
    2. 对每个 Feature 应用 filter 选项,过滤不需要的要素。
    3. 调用 geometryToLayer 将几何对象转换为 Leaflet 图层(如 Polygon, Marker)。
    4. 为图层添加 feature 属性,存储原始 GeoJSON 属性。
    5. 调用 onEachFeature 回调,允许用户自定义图层事件(如点击弹窗)

3.resetStyle(style)

  • ​​功能​​:重置图层样式到初始状态。
  • 参数​​:
    • layer:目标图层(若未指定,重置所有子图层)。
  • 流程​​:
    1. 若未指定 layer,遍历所有子图层递归调用自身。
    2. 还原图层的 optionsdefaultOptions(初始样式)。
    3. 调用 _setLayerStyle 应用 options.style 函数。

4.setStyle(style)

  • 功能​​:动态更新所有子图层的样式。
  • 参数​​
    • style:样式函数或对象。
  • 流程​​
    • 遍历所有子图层,调用 _setLayerStyle 应用新样式。

5._setLayerStyle(layer,style)

  • 内部方法​​:为单个图层设置样式。
  • 流程​​:
    1. style 为函数,执行并传入 layer.feature 获取样式对象。
    2. 调用图层的 setStyle 方法(适用于 Path 派生类如 Polyline

静态工具函数

1.geometryToLayer(featureData,options)

  • 源码
export function geometryToLayer(geojson,options){var geometry = geojson.type === 'Feature'? geojson.geometry : geojson, // 标准化几何体coords=geometry?geometry.coordinates:null,layers=[],pointToLayer=options&&options.pointToLayer,_coordsToLatLng=options&&options.coordsToLatLng||coordsToLatLng,latlng,latlngs,i,len;if(!coords && !geometry){return null}switch(geometry.type){case 'Point':latlng=_coordsToLatLng(coords);return _pointsToLayer(pointToLayer,geojson,latlng,options); // 生成 Markercase 'MultiPoint':for(i=0,len=coords.length;i<len;i++){latlng=_coordsToLatLng(coords[i]);layers.push(_pointsToLayer(pointToLayer,geojson,latlng,options));}   return new FeatureGroup(layers);case 'LineString':case 'MultiLineString':// 坐标转LatLng数组生成折线latlngs=coordsToLatLngs(coords,geometry.type === 'LineString'?0:1,_coordsToLatLng);return new Polyline(latlngs,options);   case 'Polygon':case 'MultiPolygon':// 处理多边形及孔洞latlngs=coordsToLatLngs(coords,geometry.type === 'Polygon'?1:2,_coordsToLatLng);return new Polygon(latlngs,options);case 'GeometryCollection':// 处理几何集合,递归处理子几何体for(i=0,len=geometry.geometries.length;i<len;i++){var geoLayer=geometryToLayer({geometry:geometry.geometries[i],type:'Feature',properties:geojson.properties},options);if(geoLayer){layers.push(geoLayer);}}return new FeatureGroup(layers);case 'FeatureCollection':// 处理FeatureCollection,递归处理子要素for(i=0,len=geometry.feature.length;i<len;i++){var featureLayer=geometryToLayer(geometry.features[i],options);if(featureLayer){layers.push(featureLayer)}}  return new FeatureGroup(layers);default:throw new Error('Invalid GeoJSON object.');   }
}
  • 功能:将GeoJSON几何对象转换为图层
  • ​​处理逻辑​​:
    • **点(Point)**​​:调用 _pointToLayer 生成 Marker
    • **线(LineString)**​​:坐标转 LatLng 数组,创建 Polyline
    • **面(Polygon)**​​:坐标转多维 LatLng 数组,创建 Polygon
    • **几何集合(GeometryCollection)**​​:递归处理子几何体,返回 FeatureGroup
    • **要素集合(FeatureCollection)**​​:遍历每个要素递归处理
function _pointToLayer(pointToLayerFn,geojson,latlng,options){// 优先使用用户自定义的pointToLayer,否则创建默认Marker// 若options.markersInheritOptions=true,则使用options作为Marker的选项// 否则,使用options.markers的选项return pointToLayerFn?pointToLayerFn(geojson,latlng):new Marker(latlng,options && options.markersInheritOptions&&options);
}

2.coordsToLatLngs(coords,levelsDeep,_coordsToLatLng)

  • 源码
export function coordsToLatLngs(coords,levelsDeep,_coordsToLatLng){var latlngs=[];// 递归转换坐标层级:// levelsDeep=0 → 点数组转 LatLng 数组// levelsDeep=1 → 线数组(如 MultiLineString)// levelsDeep=2 → 多边形数组(如 MultiPolygon)for(var i=0,len=coords.length,latlng;i<len;i++){latlng=levelsDeep?coordsToLatLngs(coords[i],levelsDeep-1,_coordsToLatLng):(_coordsToLatLng||coordsToLatLng)(coords[i]);latlngs.push(latlng);     }return latlngs;
}
  • 功能:将坐标数组转换为LatLng数组或多维数组

  • 参数

    • coords:坐标数组
    • levelsDeep:转换层级,0 表示点数组,1 表示线数组,2 表示面数组
    • _coordsToLatLng:自定义坐标转换函数,可选
  • 流程

    • 递归转换坐标层级:
      • levelsDeep=0:点数组转 LatLng 数组
      • levelsDeep=1:线数组(如 MultiLineString,[[lng,lat],[lng,lat]]转为[LatLng,LatLng]

3.coordsToLatLng

  • 源码
export function coordsToLatLng(coords){return new LatLng(coords[1],coords[0],coords[2]); //注意经纬度顺序调换
}
  • 功能:将 GeoJSON 坐标数组 [lng, lat, alt?] 转为 LatLng 对象。

4. latLngToCoords(latlng,precision)

  • 源码
export function latLngToCoords(latlng,precision){latlng=toLatLng(latlng)return latlng.alt !==undefined?[Util.formatNum(latlng.lng,precision),Util.formatNum(latlng.lat,precision),Util.formatNum(latlng.alt,precision)]:[Util.formatNum(latlng.lng,precision),Util.formatNum(latlng.lat,precision)]
}
  • 功能:将 LatLng 对象转为 GeoJSON 坐标数组 [lng, lat, alt?],支持精度控制。

5. latLngsToCoords(latlngs,levelsDeep,closed,precision)

  • 源码
// 处理闭合多边形
export function latLngsToCoords(latlngs,levelsDeep,closed,precision){var coords=[];for(var i=0,len=latlngs.length;i<len;i++){coords.push(levelsDeep?latLngsToCoords(latlngs[i],LineUtil.isFlat(latlngs[i])?0:levelsDeep-1,closed,precision):latLngToCoords(latlngs[i],precision))    }// 若closed=true,将第一个点复制到末尾if(!levelsDeep && closed &&coords.length>0){coords.push(coords[0].slice())   }return coords
}
  • 功能
    • 处理多边形或线数组,支持多级嵌套。
    • closed=true,将第一个点复制到末尾,形成闭合多边形。
    • 支持精度控制。

6. getFeature(layer,newGeometry)

  • 源码
export function getFeature(layer,newGeometry){return layer.feature ? Util.extend({},layer.feature,{geometry:newGeometry}):asFeature(newGeometry)
}
export function asFeature(geojson){// 统一输入为Feature对象if(geometry.type === 'Feature' || geojson.type === 'FeatureCollection'){return geojson    }return {type:'Feature',properties:{},geometry: geojson}
}
  • 功能
    • 从图层获取GeoJSON要素。
    • 若图层已有feature属性,则返回合并后的要素。
    • 若输入为GeoJSON对象,返回标准化的Feature对象。
    • 若输入为Geometry对象,返回Feature对象。

矢量图层的toGeoJSON方法

var PointToGeoJSON={toGeoJSON:function(precision){return getFeature(this,{type:'Point',coordinates:latLngToCoords(this.getLatLng(),precision)})}
}
Marker.include(PointToGeoJSON);Circle.include(PointToGeoJSON);CircleMarker.include(PointToGeoJSON);Polyline.include({toGeoJSON:function(precision){var multi = !LineUtil.isFlat(this._latlngs); // 判断是否为MultiLineStringvar coords = latLngsToCoords(this._latlngs,multi?1:0,false,precision); // 转换坐标,处理精度return getFeature(this,{type:(multi?'Multi':'')+'LineString', // 动态生成对应类型coordinates:coords })}
})Polygon.include({toGeoJSON:function(precision){var holes = !LineUtil.isFlat(this._latlngs), // 检测是否有孔洞multi=holes && !LineUtil.isFlat(this._latlngs[0]); // 检测是否为MultiPolygonvar coords=latLngsToCoords(this._latlngs,multi?2:holes?1:0,true,precision); // 处理坐标层级和闭合情况if(!holes){coords=[coords]}return getFeature(this,{type:(multi?'Multi':'')+'Polygon',coordinates:coords})}
})LayerGroup.include({toMultiPoint:function(precision){var coords=[];this.eachLayer(function(layer){coords.push(layer.toGeoJSON(precision).geometry.coordinates);})return getFeature(this,{type:'MultiPoint',coordinates:coords})},toGeoJSON:function(precision){var type =this.feature && this.feature.geometry && this.feature.geometry.type;if(type === 'MultiPoint'){return this.toMultiPoint(precision); // 若为MultiPoint,直接调用toMultiPoint方法,合并所有点图层}var isGeometryCollection = type === 'GeometryCollection',jsons=[];this.eachLayer(function(layer){if(layer.toGeoJSON){var json =layer.toGeoJSON(precision);if(isGeometryCollection){jsons.push(json.geometry);}else{var feature=asFeature(json);if(feature.type === 'FeatureCollection'){jsons.push.apply(jsons,feature.features);}else{jsons.push(feature);}}}})if(isGeometryCollection){return getFeature(this,{geometries:jsons,type:'GeometryCollection'})}return {type:'FeatureCollection',features:jsons}}
})

工厂函数

export function geoJSON(geojson,options){return new GeoJSON(geojson,options);
}export var geoJson = geoJSON;

设计亮点

1. 样式隔离​​:通过 resetStylesetStyle 实现动态样式更新,避免直接修改图层原型。
2. 递归处理​​geometryToLayercoordsToLatLngs 支持嵌套几何结构(如 MultiPolygon)。
3. 数据兼容​​asFeature 确保非标准 GeoJSON 输入(如纯几何体)被正确包装。
4. 坐标转换​​:处理 Leaflet 与 GeoJSON 的坐标顺序差异([lat, lng] vs [lng, lat]

总结

通过模块化设计,将 GeoJSON 解析、坐标转换、样式控制等功能解耦,支持灵活扩展(如自定义 pointToLayer),是 Leaflet 处理地理数据的关键模块

相关文章:

  • 信息学奥赛一本通 1535:【例 1】数列操作
  • 信奥赛-刷题笔记-队列篇-T2-P1540机器翻译和P2952Cow Line S
  • 品种亮相!贵州金桥药业筑牢市场竞争护城河
  • MATLAB语音情感识别神经网络方法
  • 从零开始:使用 Vue-ECharts 实现数据可视化图表功能
  • INT202 Complexity of Algroithms 算法的复杂度 Pt.7 NP-Completeness NP完全性
  • SwarmUI 基于.NET开发的开源AI图像生成WEB用户界面系统
  • 市政务服务技能竞赛流程策划方案
  • 【计算机视觉】OpenCV实战项目:基于Tesseract与OpenCV的字符识别系统深度解析
  • 第十七章:Llama Factory 深度剖析:易用性背后的微调框架设计
  • flutter Stream 有哪两种订阅模式。
  • 工作常用的git命令
  • Node.js中的洋葱模型
  • C++中的volatile有什么用?
  • PostgreSQL 恢复信息函数
  • flea-cache使用之Redis哨兵模式接入
  • ETL 数据集成平台与数据仓库的关系及 ETL 工具推荐
  • EDR与XDR如何选择适合您的网络安全解决方案
  • 爬虫请求频率应控制在多少合适?
  • 鸿蒙 核心与非核心装饰器
  • 习近平致电祝贺阿尔巴尼斯当选连任澳大利亚总理
  • 郑州通报涉“健康证”办理有关问题查处进展情况
  • 青海规范旅游包车行为:不得引导外省籍旅游包车违规驻地运营
  • 普京提议恢复直接谈判,泽连斯基:望俄明日停火,乌愿谈判
  • 人民日报读者点题·共同关注:今天我们为什么还需要图书馆?
  • 玉渊谭天丨中方为何此时同意与美方接触?出于这三个考虑