通过对本文的阅读,你将获取坐标系的一些基础知识,以及学会如何使用pyproj实现地理数据的投影与转换。更重要的是,作为一个开发者,面对地理坐标系的图层数据,需要进行面积计算、距离量测、规则分块等需求时,能够从容应对。
文章内容较长,先呈上目录:
1.坐标系简介
2.地图投影
3.使用pyproj实现投影与转换
4.开发中投影转换的作用(重点内容)
一、坐标系简介
地理数据本身是地球表面一个位置的映射。
但地球并不是一个正球体,而是一个两极稍扁、赤道略鼓的不规则球体。
2011年,GOCE卫星的一幅新大地水准面地图 :
想象中的地球:
创建一个能准确描述这样形状的坐标系面临着诸多挑战,主要困难包括:
(1)形状的不规则性
地球表面受到山脉、海洋深度、地形变化以及地球内部密度分布不均的影响,导致地球的形状在局部和整体上都有所差异。这种不规则性使得建立一个通用的数学模型来描述地球的形状变得复杂。
(2)重力场的复杂性
地球的重力场不是均匀的,它随位置而变化,尤其是在地形起伏较大的地区。这意味着所谓的"垂直线"(重力的方向)在地球上各处并不完全一致,这影响了测量和定位的准确性。
(3)测量误差和不确定性
实际测量地球表面和地球重力场时,会遇到各种误差来源,包括仪器精度、大气条件、观测误差等。这些不确定性增加了建立精确坐标系统的难度。
为了克服这些困难,科学家们引入了一系列的概念和模型,包括:
(1)大地水准面(Geoid)
大地水准面是理想化的海平面延伸到陆地上方形成的闭合曲面。它代表了重力等位面,即水在不受风或其他外力作用下静止时的表面。大地水准面反映了地球质量分布的不均匀性和地球自转的影响,是地球的真实形状的近似。
(2)参考椭球体(Reference Ellipsoid)
由于大地水准面非常复杂且难以直接使用,人们使用一个光滑的数学模型------参考椭球体来近似地球的形状。参考椭球体是一个旋转椭球,其长轴(赤道半径)和短轴(极半径)的比值反映了地球扁率。不同的参考椭球体用于不同的地区或不同的应用,以提高定位精度。
(3)大地坐标系(Geodetic Coordinate System)
基于参考椭球体,科学家们建立了大地坐标系,其中包括经度、纬度和大地高(相对于参考椭球体表面的高度)。大地坐标系提供了一种在全球范围内定位点的方法,尽管它们需要通过大地测量技术与大地水准面联系起来。
(4)地球中心坐标系(Geocentric Coordinate System)
为了更精确的导航和定位,尤其是卫星定位系统如GPS,使用了地球中心坐标系。这种坐标系将地球视为一个质点,使用X、Y、Z直角坐标系来描述点的位置,其中原点位于地球的质心。
(5)国际地球参考框架(ITRF)
ITRF是一种全球性的参考框架,结合了地球中心坐标系和地球自转参数,提供了全球定位和地球运动的统一标准。
无论是点、线、面、栅格还是注记,所有空间数据均在一个坐标系中创建。水平坐标系用于定位地球表面的数据,垂直坐标系用于定位数据的相对高度或深度(本文后续内容只涉及水平坐标系)。
坐标系是一个参考框架,用于定义要素在二维或三维空间中的位置。通过统一的坐标系和高程系,可以使不同源的GIS数据叠加在一起显示,以及执行空间分析。
水平坐标系可分为三种类型:地理坐标系、投影坐标系或局部坐标系。
1.1地理坐标系
**地理坐标系 (GCS)**基于三维椭球面或球面,且位置由角度测量值(通常以十进制度为单位)定义,由此测量经度(x 坐标)和纬度(y 坐标)。数据位置可表示为正数或负数:正 x 和 y 值表示赤道以北和本初子午线以东,负值表示赤道以南和本初子午线以西。
常用地理坐标系:
1.2投影坐标系
**投影坐标系 (PCS)**是使用线性测量而非角度单位的平面系统。投影坐标系由地理坐标系和地图投影共同组成。地图投影包含数学计算,用于将 GCS 的角度大地坐标转换为平面 PCS 系统的笛卡尔坐标。
投影坐标系 = 地理坐标系 + 投影方法
常用投影:
1.2.1 高斯-克吕格Gauss-Kruger投影(横轴等角切圆柱投影)
高斯-克吕格投影也被称为横轴墨卡托投影的椭圆体版本,因为其与墨卡托投影类似,不同之处在于:在高斯-克吕格投影中,圆柱与沿子午线而非赤道的球体或椭圆体相接触。生成的投影为等角投影,不会保持真实的方向。中央经线位于相关区域中心。这种中心对准方法可以最大程度减少该区域内所有属性的变形。此投影最适合于南北分布的地区。
我国规定1:1万、1:2.5万、1:5万、1:10万、1:25万、1:50万比例尺的地形图均采用高斯克吕格投影。
横轴等角切圆柱投影离开中央子午线越远,变形越大,赤道是直线。离开赤道的纬线是弧线,凸向赤道没有角度变形长度和面积变形很小。
ArcGIS中的投影坐标系(高斯克吕格投影)
示例一:CGCS2000 3度带 投影中央经线为东经102°
CGCS2000 3 Degree GK CM 102E(无带号)
CGCS2000 3 Degree GK Zone 34 (有带号)
示例二:CGCS2000 6度带 投影中央经线为东经105°
CGCS2000 GK CM 105E (无带号)
CGCS2000 GK Zone 18 (有带号)
1.2.2 阿尔伯斯投影("正轴等积割圆锥投影"、 "双轴纬线等积圆锥投影")
这种圆锥投影使用两条标准纬线,相比仅使用一条标准纬线的投影,可在某种程度上减少畸变。此投影最适用于中纬度东西方向分布的大陆板块。
阿尔伯斯投影由 Heinrich C. Albers 于 1805 年提出。椭圆体方程由 Oscar S.Adams 于 1927 年开发。
1.2.3 折 **衷投影(罗宾森投影)**伪圆柱投影
罗宾森投影既不等角也不等积。它通常会造成形状、面积、距离、方向和角度畸变。畸变模式与常见折衷伪圆柱投影类似。面积畸变随纬度增加而增大,但不受经度变化的影响。高纬度地区会被夸大。角畸变在地图中心附近适中,并向边缘增大。畸变值沿赤道和中央经线对称。
罗宾森投影主要适用于一般世界地图。
1.3局部坐标系
局部坐标系使用地球上任意位置的假原点(0, 0 或其他值)。局部坐标系通常用于大比例(小区域)映射。假定原点可能(或不可能)与已知的实际坐标对齐,但对于数据捕捉来说,可能使用局部坐标系而非全球坐标测量方位角和距离。局部坐标系通常以米或英尺表示。
二、地图投影
地图投影是把地球表面的任意点,利用一定数学法则,转换到地图平面上的理论和方法。 由于没有一种完美的方式可将曲面转置为平面而不发生任何变形,因此各种现有的不同地图投影提供了不同的属性。一些会保留面积,一些会保留局部角度,而另一些会保留特定距离或方向。
将三维地球表面的信息转换到二维平面上的过程中,如果不使用投影坐标系,那么在地图上测量距离、方向或计算面积将变得极其复杂甚至不可能。
以下是需要将地理坐标系转换为投影坐标系的一些常见情况:
(1)地图制作
制作地图时,为了保持地图的可读性和美观,需要使用投影坐标系。不同的投影方式可以强调地图的不同特性,如等角投影保持角度不变,适合航海和航空导航;等距投影保持特定方向上的距离不变,适合测量;等面积投影保持区域比例不变,适合统计和生态研究。
(2)空间数据分析
当进行GIS分析时,投影坐标系允许进行精确的距离和面积测量,这对于土地规划、资源管理、环境评估等至关重要。
在进行叠加分析、缓冲区分析、网络分析等空间分析时,投影坐标系确保了计算的准确性。
(3)工程和建筑项目
土木工程、城市规划和建筑设计中需要精确的测量,投影坐标系提供了一个稳定且精确的参考框架。
(4)遥感和卫星影像处理
遥感数据和卫星图像通常需要在投影坐标系中进行校正和分析,以便于与其他地理数据集成和比较。
(5)道路和交通网络
在设计道路和交通网络时,投影坐标系提供了准确的道路长度和交汇点位置,这对于路线规划和交通流量分析至关重要。
从地理坐标系转换到投影坐标系是必要的,以确保数据在二维平面上的正确表示和分析。然而,值得注意的是,每次投影都会引入某种类型的变形,因此选择正确的投影类型对于特定的应用场景来说非常重要。
等积投影的重要性(格陵兰岛与澳大利亚):
等角投影的重要性:
三、使用pyproj实现投影与转换
pyproj
包是Python中用于处理地理空间投影和变换的一个库,它基于PROJ(以前称为PROJ.4)库,提供了对各种坐标参考系统(CRS)的支持。pyproj
可以执行多种类型的坐标转换,如从地理坐标系(Geographic Coordinate System, GCS)到投影坐标系(Projected Coordinate System, PCS)的转换,以及从一种投影到另一种投影的转换。
我国规定按经差6和3度进行投影分带,为大比例尺测图和工程测量采用带投影。特殊情况下工程测量控制网也可用1.5度带或任意带。
以下内容或代码仅涉及常用的坐标系:
(1)CGCS2000
(2)Beijing_1954
(3)Xian_1980
(4)WGS1984
(5)New Beijing
而投影坐标系仅介绍高斯克吕格投影,其他投影可根据实际需求进行拓展。
(1)tmerc: 横向墨卡托投影
(2)merc: 墨卡托投影(正轴)
(3)lcc: 兰勃脱圆锥投影(Lambert Conformal Conic)
(4)sterea: Stereographic projection
(5)utm: UTM投影(实际上是基于横向墨卡托,但有一些特殊的参数设置)
3.1标准分带
标准分带,按经差6和3度进行投影。
pyproj
允许用户通过EPSG代码来定义和使用坐标参考系统(仅限于标准分带)。
当你使用pyproj
库中的CRS对象时,可以直接通过EPSG代码来初始化这个对象,例如:
from pyproj import CRS# 创建一个基于EPSG代码的CRS对象crs = CRS.from_epsg(3857) # 这是Web Mercator投影的EPSG代码
这样,pyproj
就利用EPSG代码加载了对应的坐标参考系统定义,并且可以使用这个定义来进行坐标转换操作。例如,你可以使用pyproj.Transformer
来创建一个转换器,将坐标从一个EPSG系统转换到另一个EPSG系统:
from pyproj import Transformer# 创建一个转换器,用于从EPSG:4326(WGS 84)到EPSG:3857(Web Mercator)的转换transformer = Transformer.from_crs("EPSG:4326", "EPSG:3857")lon, lat = -117.0, 34.0x, y = transformer.transform(lon, lat)
但这样的方式,需要我们提前知道数据在3°或6°带所对应的EPSG代码,这对于一小部分人来说,是有困难的。故此,小编提供了根据数据所在的经度值(或覆盖的范围),计算多种坐标系下EPSG代码。
@classmethoddef calculate_epsg(cls, longitude, coords_code, zone_width): cls._check_params(longitude, coords_code, zone_width) if coords_code == 4490: base_epsg = 4509 if zone_width == 6 else 4548 base_central_meridian = 117 elif coords_code == 4326: base_epsg = None # This case does not apply to the EPSG calculation logic used. base_central_meridian = None elif coords_code == 4610: base_epsg = 2343 if zone_width == 6 else 2380 base_central_meridian = 105 elif coords_code == 4214: base_epsg = 21458 if zone_width == 6 else 2432 base_central_meridian = 105 elif coords_code == 4555: base_epsg = 4584 if zone_width == 6 else 4792 base_central_meridian = 105 else: base_epsg = None base_central_meridian = None if base_epsg is not None and base_central_meridian is not None: central_meridian = cls._calculate_zone(longitude, zone_width) epsg_code = cls._calculate_epsg(base_epsg, base_central_meridian, central_meridian, zone_width) elif coords_code == 4326: pp = int(longitude / 6) + 31 epsg_code = f"326{pp}" else: # epsg_code = 102100 epsg_code = 3395 return epsg_code
3.21.5度带或任意带
(1)1.5度带的使用场景
1.5度带是指高斯-克吕格投影中每个投影带覆盖1.5度的经度范围。这种带宽被广泛使用于那些地形复杂、对地图精度要求较高的区域。
1.5度带能够更好地满足大比例尺制图的需求,尤其是在山区和城市规划中,减少因投影造成的误差。
(2) 任意带的使用场景
任意带的概念指的是在高斯-克吕格投影中,可以自定义中央经线和带宽,而不是严格遵守3度或1.5度的固定带宽。任意带的使用场景通常包括:
1)特定项目需求:当一个项目区域跨越了多个标准带时,为了简化数据管理和坐标转换,可能会选择一个任意带,其中央经线正好穿过项目区域的中心,以最小化整个项目区域内的投影变形。
2)特定研究区域:在科学研究中,尤其是地质勘探、环境监测或资源调查等领域,研究者可能根据研究区域的特定需求选择任意带,以确保数据的高精度和一致性。
3)国家或地区标准:有些国家或地区可能根据自己的地理特点和需求,采用不同于国际标准的任意带系统。例如,德国在其国家坐标系中使用了2度带宽的高斯-克吕格投影。
总之,1.5度带和任意带在高斯-克吕格投影中的使用,主要取决于具体的地理条件、项目需求和精度要求。
创建自定义投影示例代码如下:
@classmethoddef create_custom_projection(cls, center_longitude, coords_code=4490, zone_width=1.5): """Create a custom projection based on the provided parameters.""" center_longitude = cls._calculate_zone(center_longitude, zone_width) # Retrieve ellipsoid parameters if coords_code in code_params: params = code_params[coords_code] a = params["a"] rf = params["rf"] else: print(f"Ellipsoid '{coords_code}' not found in ellipsoid_params.") return None # Construct the projection definition string ''' tmerc: 横向墨卡托投影 merc: 墨卡托投影(正轴) lcc: 兰勃脱圆锥投影(Lambert Conformal Conic) sterea: Stereographic projection utm: UTM投影(实际上是基于横向墨卡托,但有一些特殊的参数设置) ''' center_longitude = int(float(center_longitude)) if float(center_longitude).is_integer() else float(center_longitude) ellipsoid = code_name[coords_code] custom_proj_definition = ( f"+proj=tmerc +lat_0=0 +lon_0={center_longitude} +k=1 +x_0=500000 +y_0=0 " f"+a={a} +rf={rf} +units=m +no_defs +proj_info:({ellipsoid} / {zone_width}-degree Gauss-Kruger CM {center_longitude}E) +coords_code:[{coords_code}]" ) try: custom_crs = CRS(custom_proj_definition) except Exception as e: print(f"Error creating CRS: {e}") return None return custom_crs
四、开发中投影转换的作用
我们对数据进行分析处理时,有时候数据的覆盖区域很大(如全省、全国),或者获取的数据源本身范围不大,但坐标系就是地理坐标系。
当涉及到大面积的数据分析,尤其是需要进行精确的面积计算、距离量测、栅格分析等操作时,地理坐标系(如WGS84)往往不能满足需求,因为它们基于球面或椭球体,而这些几何形状上的测量会产生显著的误差。因此,进行投影转换变得至关重要。
以下是几个补充点说明为什么需要这种转换:
(1)减少变形地理坐标系在全球范围内保持方向和角度的一致性,但在测量距离和面积时会因地球曲率而产生变形。通过选择适当的投影,可以最小化特定区域内的变形,使得计算更加准确。
(2)提高效率
在地理坐标系中进行计算通常比在投影坐标系中复杂且耗时,因为后者可以简化为平面坐标系,使得计算更为直接和快速。
(3)适应具体需求不同的应用场景可能需要不同的投影类型。例如,等角投影适用于导航,等积投影适用于面积测量,而方位投影则适合极地研究。自定义投影可以根据特定的分析需求优化这些特性。
(4)数据整合当分析的数据来自多个不同投影的数据集时,将所有数据转换到一个共同的投影中可以方便数据的整合和对比,避免因投影差异导致的分析误差。
(5)可视化效果投影选择还影响地图的视觉呈现,某些投影更适合展示特定的地理特征或模式,有助于更好地传达分析结果。
(6)标准化与兼容性使用统一的投影系统可以确保数据在不同平台和软件之间的一致性和可互换性,这对于协作和数据共享尤其重要。
因此,在数据分析流程中动态地进行投影转换,而不是预先转换所有数据,可以提供更大的灵活性,减少存储和管理多种投影数据集的负担,并确保分析结果的精确性和相关性。这尤其适用于那些覆盖大范围或有特殊精度需求的项目。
以下给出一些平时写脚本工具常碰到的场景:
01计算两条线之间的距离
def cal_distance_between_line(line1, line2, spa_type): if spa_type == "Projected": return line1.distance(line2) else: geom1, geom2 = nearest_points(line1, line2) # 近似计算测地线 return geodesic((geom1.y, geom1.x), (geom2.y, geom2.x)).meters
02arcpy计算两个点之间的距离
def cal_distance(geom1, geom2, spatial_ref): spa_type = spatial_ref.type if spa_type == "Projected": # 将 Point 对象转换为 PointGeometry 对象,然后计算距离 point_geom1 = arcpy.PointGeometry(geom1, spatial_ref) point_geom2 = arcpy.PointGeometry(geom2, spatial_ref) return point_geom1.distanceTo(point_geom2) else: return geodesic((geom1.Y, geom1.X), (geom2.Y, geom2.X)).meters
03按投影带对数据进行投影并计算
class Geographic2Project(GeoFC, FormatEpsg): def __init__(self, _in_fc, zone_width): super().__init__(_in_fc) self.fc_name = os.path.basename(self.fc).split('.')[0] self.gdf = self.read_fc(include_oid=False) self.zone_width = zone_width self.gdf_code = self.gdf.crs.to_epsg() self.geo_type = self.gdf.geometry.geom_type.unique()[0].lower() self.cal_group() # 按照 epsg_code 分类进行投影到投影坐标系 @classmethod def apply_projection(cls, group, transformer, crs): # 应用投影函数到整个几何列 group['geometry'] = group['geometry'].apply(cls.project_geometry, args=(transformer, cls.geo_type)) # 处理投影后的几何体 group['geometry'] = group['geometry'].apply(cls.process_geometry) # 移除空几何体 group = group.dropna(subset=['geometry']) # 生成一个新的 gdf,包含投影坐标系中的几何信息 projected_gdf = gpd.GeoDataFrame(group, geometry='geometry', crs=crs) return projected_gdf if not projected_gdf.empty else None def cal_group(self): # 确认 gdf 的坐标系类型 if self.gdf.crs.is_projected: raise ValueError("GeoDataFrame is already in a projected coordinate system. No projection needed.") elif self.gdf.crs.is_geographic: gdf_code = self.gdf.crs.to_epsg() geo_type = self.gdf.geometry.geom_type.unique()[0].lower() # 计算每个点位的投影带 EPSG 代码 if geo_type == "point": self.gdf['longitude'] = self.gdf.geometry.x else: self.gdf['longitude'] = self.gdf.geometry.apply(lambda geom: geom.centroid.x) if self.zone_width in [3, 6]: self.gdf['epsg_code'] = self.gdf['longitude'].apply(self.calculate_epsg, coords_code=gdf_code, zone_width=self.zone_width) else: self.gdf['epsg_code'] = self.gdf['longitude'].apply( lambda lon: str(self._calculate_zone(lon, zone_width=self.zone_width))) else: raise ValueError("Unsupported CRS type. Only projected or geographic coordinate systems are supported.") def geographic_to_project(self): # 按照 epsg_code 分类进行投影到投影坐标系 projected_gdfs = {} for epsg_code, group in self.gdf.groupby('epsg_code'): # 解析epsg_code,用于区分按3°、6°,还是其他经度跨度进行的投影 if abs(float(epsg_code)) > 180: epsg_code = int(epsg_code) custom_proj = f"EPSG:{epsg_code}" transformer = Transformer.from_proj(f"EPSG:{self.gdf_code}", custom_proj, always_xy=True) else: center_longitude = int(float(epsg_code)) if float(epsg_code).is_integer() else float(epsg_code) custom_proj = self.create_custom_projection(center_longitude, self.gdf_code, zone_width=self.zone_width) transformer = Transformer.from_proj(f"EPSG:{self.gdf_code}", custom_proj, always_xy=True) # 按照 epsg_code 分类进行投影到投影坐标系 projected_gdf = self.apply_projection(group, transformer, custom_proj) projected_gdfs[epsg_code] = projected_gdf # 移除空值(跳过 EPSG:4326) projected_gdfs = {k: v for k, v in projected_gdfs.items() if v is not None} return projected_gdfs def project_export(self, out_path=None, out_name=None, columns_to_remove=None, export=False): projected_gdfs = self.geographic_to_project() if export is True: if out_path is None: raise ValueError("输出目录不能为空!") export_obj = ExportData() export_obj.gdf_to_gdb(projected_gdfs, out_path, out_name, columns_to_remove, fc_name=self.fc_name) return projected_gdfs
04将投影坐标系数据转换为地理坐标系数据
class Project2Geographic(FormatEpsg): def __init__(self, gdf_or_dict): super().__init__() self.gdf_or_dict = gdf_or_dict def project_to_geographic(self): if isinstance(self.gdf_or_dict, dict): # Create a defaultdict to store GeoDataFrames grouped by geographic CRS code grouped_gdfs = defaultdict(list) # Iterate over each key-value pair in the dictionary for zone, gdf in self.gdf_or_dict.items(): if gdf.crs.is_projected: target_code = self.get_geographic_crs_code(gdf.crs) gdf = gdf.to_crs(target_code) grouped_gdfs[target_code].append(gdf) elif gdf.crs.is_geographic: grouped_gdfs[gdf.crs.to_epsg()].append(gdf) else: raise ValueError( f"Unsupported CRS type for zone {zone}. Only projected or geographic coordinate systems are supported.") # Process each group of GeoDataFrames result_gdfs = {} for target_code, group_gdfs in grouped_gdfs.items(): # Combine all GeoDataFrames in the same group into one combined_gdf = gpd.GeoDataFrame(pd.concat(group_gdfs, ignore_index=True), crs=f'EPSG:{target_code}') result_gdfs[int(target_code)] = combined_gdf return result_gdfs elif isinstance(self.gdf_or_dict, gpd.GeoDataFrame): gdf = self.gdf_or_dict if gdf.crs.is_projected: target_code = self.get_geographic_crs_code(gdf.crs) geo_type = gdf.geometry.geom_type.unique()[0].lower() transformer = Transformer.from_crs(gdf.crs.to_epsg(), f"EPSG:{target_code}", always_xy=True) if geo_type == "point": def project_geometry(geometry): lon, lat = transformer.transform(geometry.x, geometry.y) return Point(lon, lat) else: def project_geometry(geometry): project = partial(transformer.transform, direction='forward') return transform(project, geometry) gdf['geometry'] = gdf['geometry'].apply(project_geometry) gdf.crs = f'EPSG:{target_code}' return gdf elif gdf.crs.is_geographic: return gdf else: raise ValueError("Unsupported CRS type. Only projected or geographic coordinate systems are supported.") else: raise ValueError("Input must be either a GeoDataFrame or a dictionary of GeoDataFrames.") def geographic_export(self, out_path, out_name=None, columns_to_remove=None, export=False): geographic_gdfs = self.project_to_geographic() if export is True: if out_path is None: raise ValueError("输出目录不能为空!") export_obj = ExportData() export_obj.gdf_to_gdb(geographic_gdfs, out_path, out_name, columns_to_remove) return geographic_gdfs
05投影转换示例
小编编写了一个使用开源的方式读取要素图层数据,并按指定分带或自定义分带的方式,对图层数据进行投影变换,最终可根据需求进行其他运算分析或导出到gdb中。
调用方式如下:
if __name__ == '__main__': in_feature = r"D:\7.python 工具箱\工具箱部署\3.测试数据\水系线\云南省_水系线数据_4490.shp" zone_width = 3 out_path = r"D:\7.python 工具箱\工具箱部署\3.测试数据\水系线\scratch.gdb" out_name = None columns_to_remove = ['longitude', 'epsg_code'] # 对输入要素按投影带进行分组,分组投影后导出 to_project_obj = Geographic2Project(in_feature, zone_width) project_gdfs = to_project_obj.project_export(out_path, out_name, columns_to_remove, export=True) # 又将分组投影后导出后的结果合并,变换为地理坐标系导出 to_geographic_obj = Project2Geographic(project_gdfs) project_gdfs = to_geographic_obj.geographic_export(out_path, out_name, columns_to_remove=None, export=True)
按3°带,使用高斯克吕格投影对云南省水系数据进行投影并输出。(数据来源:【立方数据学社】)
按6°带,使用高斯克吕格投影对云南省水系数据进行投影并输出。
按1.5°带,使用高斯克吕格投影对云南省水系数据进行投影并输出。
有需要这方面技术交流的,可关注"GIS探案"后,私信小编。