源码分析之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){
      // 统一处理数组,遍历features
      var 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);  
      }
      
      // 将图层添加到FeatureGroup
      return 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); // 生成 Marker
     case '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); // 判断是否为MultiLineString
       var 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]); // 检测是否为MultiPolygon

      var 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 处理地理数据的关键模块

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jinuss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值