JSON 与 GeoJSON 区别:坐标顺序、几何规则与空间数据排查
发布时间:2026/10/1 16:20:26来源:尧图网络
说到 JSON 和 GeoJSON很多人第一反应是这不就是一个东西吗GeoJSON 不就是加了坐标的 JSON。这话对了一半。JSON 是一套通用的数据交换语法GeoJSON 是在这套语法上叠加了一层地理语义的约定。真正要命的地方在于JSON 只要求你语法合法而 GeoJSON 在语法之外还要求你遵守几何规则、坐标顺序、环方向、坐标基准这一堆隐性契约。语法不合法解析器直接报错语义不合法解析器一声不吭地把数据放过去然后你的点跑到南极洲附近、多边形变成一个蝴蝶结、地图上什么都没渲染出来。前者好排查后者能耗掉你一整天。这篇文章想聊的不是JSON 是什么这种入门科普而是把 JSON 和 GeoJSON 放在一起拆开看它们的共同点在哪GeoJSON 额外加了哪些约束这些约束在真实工程里会在什么地方咬你一口以及遇到了该怎么一步步排查。适合已经在用地图、做数据可视化、写后端接口或者处理空间数据的人看也适合刚学完 JSON 语法想往地理方向走的朋友。下面全部基于我在实际项目里踩过的坑和常用的处理链路来讲能直接抄的代码和命令我都贴上。1. 从那个坐标顺序被写反的下午说起1.1 一个点飞到南极洲的真实经历早期做数据可视化的时候我把一批城市点位从数据库导出来字段分别是lng和lat然后随手拼成了[lat, lng]。渲染出来的时候地图是空的我以为图层没加载上翻控制台、翻网络请求折腾了半天才发现前端库读的坐标顺序是[经度, 纬度]而我给的是反的。纬度取值范围是 -90 到 90经度是 -180 到 180我把一个纬度 39 的点放到了经度位置前端把它当成东经 39 度纬度位置放的是 116超过了纬度上限很多解析器不校验这个范围直接算出一个天文数字的投影坐标点就被扔到可见区域之外了。地图上看起来什么都没画实际上是画在了你看不到的地方。这件事让我意识到一个关键区别JSON parser 不关心你的数据是什么意思GeoJSON 的消费端却很关心。同一份数据用json.load读进来一切正常用地图库渲染就出问题。问题不在 JSON 层在 GeoJSON 的语义层。1.2 GeoJSON 在语法层确实就是 JSON先把最基础的事实钉死GeoJSON 是一份完全合法的 JSON 文档。它没有引入任何新的语法元素——没有新类型、没有新括号、没有新转义规则。你把它丢给任意一个标准 JSON 解析器只要 GeoJSON 写作规范解析一定会成功。反过来说任何一个能解析 JSON 的库都不需要额外适配就能读 GeoJSON 的文本结构。这也是它当年能迅速铺开的原因不需要为它发明一套二进制格式或者类 XML 的方言浏览器、Python、Node、Java、Go 里现成的 JSON 能力全部可以直接用。相比老一代的 Shapefile.shp .shx .dbf 三件套编码混乱字段名限 10 个字符、KMLXML 系体积大、GML更啰嗦GeoJSON 的零门槛优势非常明显一个文本文件、拖进去就能用。所以讨论两者的差异层次要分清楚层次JSONGeoJSON语法层定义完整完全继承 JSON无新增语法数据类型层6 种值类型复用同样 6 种类型语义层无约定 type 取值、坐标含义、环方向、坐标基准校验层语法校验即可语法校验 几何规则校验生态层通用工具链通用工具链 GIS 工具链看这张表就明白了绝大部分GeoJSON 出错的问题都出在后三行而不是第一行。1.3 什么时候该用 GeoJSON什么时候不该用不是所有带坐标的数据都值得写成 GeoJSON。我一般的判断标准是这样的数据需要在多种 GIS 软件和前端地图库之间流转用 GeoJSON兼容性最好。数据只是内部接口传参字段结构还在频繁调整用普通 JSON 更灵活GeoJSON 的字段约定反而会限制你。数据量在几十万要素以上考虑换 TopoJSON、FlatGeobuf 或者干脆用瓦片服务GeoJSON 的体积在浏览器里会很吃紧。需要带大量业务属性做查询分析导入 PostGIS 之类的空间数据库比在 GeoJSON 里硬扛更合适。这个判断很重要见过太多团队为了看起来专业把一堆简单配置塞成 GeoJSON结果维护成本翻倍。工具是为场景服务的别反过来。2. JSON 本身的语法边界与解析报错的真实成因2.1 六种值类型和三条容易忘的硬规则JSON 的值类型只有六种object、array、string、number、boolean、null。听起来简单但有三条硬规则经常被违反字符串必须用双引号单引号不是合法 JSON。对象和数组的最后一个元素后面不能有逗号trailing comma。数字不支持NaN、Infinity、-Infinity也不支持十六进制字面量。这里有个非常隐蔽的坑不同语言的标准库对规则的严格程度不一样。Python 的json.loads默认接受NaN和Infinity这并不符合 JSON 规范。我遇到过 Python 服务端序列化出的数据里带了NaN自己读回来一切正常丢给浏览器JSON.parse直接抛Unexpected token N排查了很久才发现是数值计算里出现了Infinity。import json # Python 默认允许但这不符合 JSON 规范 print(json.loads({v: NaN})) # {v: nan} # 想严格按规范来加这个参数 try: json.loads({v: NaN}, parse_constantlambda x: (_ for _ in ()).throw(ValueError(x))) except ValueError as e: print(非法常量:, e)2.2 unterminated string at position 8192 这类报错怎么读Unterminated string in JSON at position 8192 (line 1 column 8193)是个特别典型的错误。8192 这个数字非常有辨识度它等于 8 × 1024通常意味着你在某个缓冲区边界上把数据截断了。常见的成因有几个HTTP 响应分块读取时没有拼完整、读取流的时候按固定长度切割、日志系统截断超长字段、某些网关对响应体做了长度限制。这时候不要盯着 JSON 格式看格式多半没问题问题在于你拿到的文本本身就是残缺的。检查顺序我一般是这样打印出字符串的实际长度和Content-Length对比。看字符串结尾是不是停在半句中间比如停在某个字段名或者半个转义序列里。检查是不是流式读取没有做完整的拼接。用text[-100:]看尾部如果尾部缺失闭合括号基本可以确认是截断。跟这个错误经常一起出现的还有Failed to deserialize the json body into the target type: input: missing field xxx。这类报错来自强类型语言的序列化框架比如 Rust 的 serde、Java 的 Jackson 严格模式意思是 JSON 本身解析成功了但你声明的目标类型要求某个字段必须存在而实际数据里没有。这属于结构契约不匹配不是语法问题。解决办法要么给字段加默认值要么让上游补齐字段别去改解析器配置绕过去否则数据缺陷会被吞掉后面更难查。2.3 别用肉眼读嵌套结构这是我一直强调的一条超过两层的嵌套 JSON不要靠眼睛找问题。一份几千行的 JSON你盯半小时也看不出哪个括号对不上。用工具命令行快速看结构jq .或者python -m json.tool data.json。编辑器插件做格式化和折叠VS Code 自带格式化对 JSON 已经够用。真实数据里如果字段很多用jq paths快速列出所有路径比逐个展开快得多。# 校验并格式化失败会直接告诉你出错的字节位置 python -m json.tool data.json /dev/null # 提取所有键路径快速摸清结构 jq -r paths | join(.) data.json | sort -u # 只看 GeoJSON 里的 geometry 类型分布 jq -r .features[].geometry.type data.geojson | sort | uniq -c提示python -m json.tool报错时会指出行列号配合编辑器的跳转到行功能定位速度比任何手动查找都快。2.4 数字精度JSON 不区分整数和浮点JSON 规范里数字就是一个 number没有 int 和 float 的区分但几乎所有解析器都会按双精度浮点IEEE 754来处理。这意味着大整数会丢精度。典型场景是数据库主键用了 18 位以上的长整型 ID序列化成 JSON 再解析回来末几位变了。处理方式是这类 ID 一律序列化成字符串别用数字传。这个特性在 GeoJSON 里更重要因为坐标本身就是浮点。后面第 4 章会专门算一下精度截断对坐标距离的影响这里的结论先记着JSON 的数字层面对地理数据来说精度是够的但要小心序列化环节的格式化行为。3. GeoJSON 给 JSON 额外叠加的那些私货3.1 type 字段九种取值和两层对象模型GeoJSON 的核心是把数据分成两类对象几何对象和要素对象。几何对象有七种Point单个点coordinates是[x, y]。MultiPoint点数组。LineString线至少两个点。MultiLineString线数组。Polygon面坐标是环的数组第一个环是外环后面的是内环洞。MultiPolygon面数组嵌套又深一层。GeometryCollection几何集合用geometries字段而不是coordinates。要素对象有两种Feature必须是{type:Feature, geometry: ..., properties: ...}geometry可以是上面七种之一也可以是null。FeatureCollection必须有features数组每个元素是一个 Feature。{ type: FeatureCollection, features: [ { type: Feature, id: 1, properties: { name: 示例点, category: poi }, geometry: { type: Point, coordinates: [116.397, 39.908] } }, { type: Feature, properties: { name: 示例面 }, geometry: { type: Polygon, coordinates: [ [[116.0, 39.0], [116.2, 39.0], [116.2, 39.2], [116.0, 39.2], [116.0, 39.0]] ] } } ] }这九种类型必须记住因为它们直接决定了coordinates的嵌套深度Point 是 1 层数组LineString 是 2 层Polygon 是 3 层MultiPolygon 是 4 层。这是实际处理中最容易写错循环的地方。我常用的办法是按类型写一个深度映射表处理前先查表避免写一堆if type ...的嵌套判断。3.2 坐标必须是 [经度, 纬度]没有商量余地现行规范里明确规定坐标顺序是[longitude, latitude]也就是 x 在前、y 在后。这条规定和很多国内开发者的直觉相反因为我们平时说经纬度是经度在前但写代码时很多人习惯性写lat, lng因为数据库字段名常常是latitude, longitude的顺序。更麻烦的是历史遗留早期的 GeoJSON 草案允许通过crs字段自定义坐标参考系位置顺序也不那么明确。虽然现在规范已经明确取消了crs字段、统一到 WGS84 十进制度但网上依然有大量老代码、老数据带着crs字段。碰到这种数据处理方式是把crs忽略掉然后确认坐标数值本身是不是 WGS84。如果原本是别的投影坐标比如高斯克吕格、Web Mercator 的米制坐标直接当经纬度用会得到完全错误的位置。坐标顺序错误有个很好用的自检方法看两个数值的分布范围。如果第一个数值大量落在 ±90 之外那几乎可以确定是纬度放在了前面。写个统计脚本一分钟就能判断import json def collect_xy(obj, out): if isinstance(obj, dict): if obj.get(type) in (Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon): def walk(c): if isinstance(c, list) and c and isinstance(c[0], (int, float)): out.append(c) elif isinstance(c, list): for i in c: walk(i) walk(obj.get(coordinates)) for v in obj.values(): collect_xy(v, out) elif isinstance(obj, list): for v in obj: collect_xy(v, out) pairs [] with open(data.geojson, encodingutf-8) as f: collect_xy(json.load(f), pairs) first [p[0] for p in pairs] second [p[1] for p in pairs] print(第一个分量范围:, min(first), max(first)) print(第二个分量范围:, min(second), max(second))3.3 多边形环的方向和闭合规范里写得很细但很多人不读Polygon 的coordinates是环的数组。规范里有两条硬要求第一个环是外环后续环是内环洞。外环按逆时针右手法则走内环按顺时针走。每个环的首尾坐标必须相同也就是环必须是闭合的。真实情况是绝大多数解析器和渲染库对环方向是宽容的——你给顺时针的外环它照样渲染出来。但一旦要做空间分析比如用 PostGIS 的ST_Area计算面积方向错了会得到负值或者用某些库做叠加分析时结果完全不对。环不闭合就更直接了ST_IsValid会直接判为无效几何。所以稳妥的做法是处理完数据后跑一次有效性检查。Python 侧用 shapely 最方便from shapely.geometry import shape import json with open(data.geojson, encodingutf-8) as f: gj json.load(f) for feat in gj[features]: if not feat.get(geometry): continue geom shape(feat[geometry]) if not geom.is_valid: print(无效几何:, feat.get(properties), geom.is_valid_reason if hasattr(geom, is_valid_reason) else 自相交或环未闭合) if not geom.is_simple: print(存在自相交:, feat.get(properties))3.4 properties、id、bbox哪些必须写哪些写了要负责properties是挂业务属性的地方规范要求它必须是 object 或 null。不能是数组、字符串、数字。这一条经常被违反因为有人图省事写成properties: [a, b]或者properties: some text。严格校验的库会直接拒收。id是可选字段可以是字符串或数字用来标识要素。注意id应当唯一但规范不强制。做数据合并的时候我会主动给每个要素生成稳定的 id便于后续增量更新和去重。bbox是包围盒格式是[west, south, east, north]注意它的顺序和坐标一样是 x 在前。bbox可以出现在顶层、Feature 上、或者 Geometry 上。有了它渲染和空间查询可以先做粗略的矩形过滤省掉大量计算。规范允许你省略bbox库会自己算但如果数据要频繁被空间查询建议预处理时算好写进去。还有一点GeoJSON 的顶层必须是一个对象不能是数组。你不能返回[{...}, {...}]这种裸数组必须是FeatureCollection。这个错误在 REST 接口里出现频率极高因为开发者习惯了列表接口直接返数组。4. 把 JSON 和 GeoJSON 摆在一起逐项对比4.1 结构自由度与语义约束的取舍这是两者最本质的差异。JSON 是纯结构格式它只管括号配对、引号闭合、逗号位置至于键名叫什么、值的含义是什么完全不管。你可以写{a: 1}也可以写{经纬度: 北京}JSON 层面都合法。GeoJSON 把一部分结构固定下来了type必须是指定枚举值coordinates必须是数字嵌套数组features必须是数组properties必须是对象或 null。这些约束让不同工具之间可以互相理解代价是灵活性。你想在顶层塞个metadata字段放数据版本、坐标系说明、生成时间规范并不禁止但严格校验的工具可能会警告放在properties里又显得别扭。我的处理方式是顶层加自定义字段没问题但别覆盖type、features、coordinates、geometry、properties这几个保留字。自定义字段加个前缀比如x_meta避免未来和规范新增字段撞车。4.2 体积、精度和坐标顺序的三组实际数据第一组是精度。GeoJSON 坐标是十进制度1 度纬度对应的地面距离约为 111.32 公里。换算下来小数位数地面精度纬度方向4 位约 11 米5 位约 1.1 米6 位约 0.11 米7 位约 1.1 厘米经度方向的精度还要乘以该纬度上的余弦值在北纬 40 度附近经度 1 度只有大约 85 公里。所以同一份数据里经度方向的实际精度比纬度方向更高截断到 6 位小数时经度方向的误差大约 0.085 米。一般情况下6 位小数对绝大多数业务场景都够了7 位已经是厘米级超过 7 位基本没有意义纯属浪费体积。我做过一次实测把一份含 1.2 万个点的数据从 15 位小数截断到 6 位文件从 4.8MB 降到 2.6MB接近一半。渲染结果肉眼看不出差别。这就是精度控制的性价比。第二组是体积对比。同样的几何数据用 GeoJSON 和 TopoJSON 存后者通常能小很多因为 TopoJSON 把共享边界提取成 arc相邻多边形复用同一段弧线的坐标而 GeoJSON 每个多边形都完整存一遍边界。锯齿状边界多的行政区域数据压缩比尤其明显。我的经验是典型行政区划数据能压到原体积的 20% 到 40%。不过 TopoJSON 不是 GeoJSON 的超集前端库需要额外转换这一点要权衡。第三组是坐标顺序的验证成本。JSON 里数组顺序完全由你自己定义前后端对齐就行GeoJSON 的顺序是规范强制的写错了不会有报错只会在渲染时体现为点不见了。所以我会在 CI 里加一个范围检查所有坐标的第一个分量必须在 ±180 内第二个分量必须在 ±90 内超了直接构建失败。4.3 校验工具链的差异JSON 的校验生态很成熟JSON Schema 是通用方案主流的语言都有对应实现。你可以定义一套 Schema 来约束业务字段类型、必填项、取值范围然后集成到接口层做请求校验非常顺畅。GeoJSON 的校验要分两步走。第一步是语法校验直接用 JSON 解析器第二步是几何规则校验需要专门的库或者自己写规则。常用的组合JavaScriptturf/helpers配合turf/invariant做几何断言或者用geojson-validation做结构校验。Pythonjsonschema配 GeoJSON 的官方 Schema 文件再加上shapely做几何有效性检查。命令行ogrinfo可以读 GeoJSON 并输出几何类型和范围快速确认解析是否正确。# 快速看一份 GeoJSON 的概要图层名、要素数、几何类型、范围、坐标系 ogrinfo -so -al data.geojson # 输出成其他格式顺便验证能不能被 GDAL 正确读取 ogr2ogr -f FlatGeobuf out.fgb data.geojson注意单纯用JSON.parse通过的 GeoJSON只说明语法没问题不代表几何合法。做入库或者做空间分析之前几何有效性检查这一步别省。5. 一次完整的数据处理链路从解析到上屏5.1 读取阶段编码、流式读取和异常兜底GeoJSON 规范要求 UTF-8 编码。实际项目中我依然遇到过 GBK 文件、带 BOM 的文件。带 BOM 的 UTF-8 文件用某些解析器会在开头报错因为 BOM 字符不是合法 JSON 起始符。处理方式很简单import json def load_geojson(path): with open(path, rb) as f: raw f.read() # 去掉 UTF-8 BOM if raw.startswith(b\xef\xbb\xbf): raw raw[3:] return json.loads(raw.decode(utf-8))大文件的处理要换个思路。几十兆的 GeoJSON 直接json.load会把整个结构建在内存里Python 里一个点要素解析成 dict 后的内存占用是文本体积的好几倍几百兆的文件能把机器拖死。这时候用流式解析ijson是 Python 里比较常用的选择只在需要的时候构造几何对象边读边处理边丢弃。import ijson def iter_features(path): with open(path, rb) as f: for feat in ijson.items(f, features.item): yield feat for feat in iter_features(big.geojson): # 处理单个要素处理完即释放 passNode 侧对应的是stream-jsonRust 侧有serde_json的StreamDeserializer思路都一样不一次性构建完整对象树。5.2 几何阶段的常见操作和顺序拿到数据之后通常要做几件事坐标精度截断、几何简化、有效性修复、剔除空几何。顺序很重要我一般按这个来剔除geometry为 null 或者坐标为空的要素先减小数据量。修复无效几何自相交、环未闭合因为简化操作对无效几何的行为不可预期。做几何简化减少顶点数。截断坐标精度减小文本体积。重新计算bbox。简化这一步在 Python 里用 shapely 的simplify很直接容差按你要控制的精度来定。如果要求误差不超过 1 米纬度方向 1 米约等于 0.000009 度容差给这个量级。但要注意简化可能把多边形压成非法几何所以简化后还要再检查一遍。from shapely.geometry import shape, mapping def clean_geom(geom, tol1e-5): if geom.is_empty: return None if not geom.is_valid: geom geom.buffer(0) # 常见的修复手段 if geom.is_empty: return None simplified geom.simplify(tol, preserve_topologyTrue) return simplified if not simplified.is_empty else Nonepreserve_topologyTrue这个参数很关键。不开启的话简化算法可能让多边形自相交或者让相邻面之间产生缝隙视觉上会出现细碎的黑边或者空洞。5.3 入库阶段PostGIS 和字段映射的坑把 GeoJSON 导入空间数据库是高频操作。PostGIS 提供了直接的支持-- 从文本构造几何SRID 明确指定为 4326 SELECT ST_GeomFromGeoJSON({type:Point,coordinates:[116.397,39.908]}); -- 建表时明确几何类型和 SRID比用通用 GEOMETRY 类型性能和约束都更好 CREATE TABLE poi ( id bigserial PRIMARY KEY, name text, geom geometry(Point, 4326) );这里有两个坑。第一ST_GeomFromGeoJSON默认认为输入是 WGS84 经纬度如果数据实际是投影坐标必须先转换再入库否则位置全错。第二几何类型别用无参数的GEOMETRY明确写Point、Polygon这类具体类型数据库才能建对应的空间索引和约束查询性能差异很大。另外如果 GeoJSON 里有三维坐标[x, y, z]PostGIS 会识别为 Z 坐标。有些分析函数的 Z 处理行为和预期不同导入前确认清理掉不需要的第三维能省不少麻烦。5.4 渲染阶段前端库加载时的实际问题前端两个主流路线Canvas/SVG 系的 LeafletWebGL 系的 Mapbox GL、MapLibre、deck.gl。Leaflet 加载 GeoJSON 非常直接fetch(/data.geojson) .then((r) { if (!r.ok) throw new Error(HTTP r.status); return r.text(); }) .then((text) { let data; try { data JSON.parse(text); } catch (e) { console.error(JSON 解析失败:, e.message); console.error(文本长度:, text.length); console.error(末尾 120 字符:, JSON.stringify(text.slice(-120))); return; } L.geoJSON(data, { style: { color: #2a6, weight: 1 }, onEachFeature: (feature, layer) { if (feature.properties feature.properties.name) { layer.bindPopup(feature.properties.name); } }, }).addTo(map); });那段catch里的日志很值得保留。解析失败时把长度和末尾内容打出来能立刻判断是截断还是格式问题比在控制台里只看到一个SyntaxError有用得多。WebGL 系加载时要注意的是数据量。几万个点用 GeoJSON source 还可以几十万个点会明显卡顿。这时候的常规做法是转成矢量瓦片让服务端按缩放级别切分前端只加载当前视野内的部分。如果暂时不想上瓦片服务deck.gl 的ScatterplotLayer用二进制数组喂数据比 GeoJSON 对象数组快很多代价是要自己把坐标转成类型化数组。还有一点很容易忽略渲染时坐标顺序是 [经度, 纬度]但很多绘制 API 的参数是 (lat, lng) 顺序。你在同一个文件里可能既调用 GeoJSON 相关接口又调用第三方 API两者的习惯不一致复制粘贴代码时最容易出错。我养成了一个习惯变量名里带上单位叫lngLatPair或者latLngPair看到变量名就知道顺序能挡掉一部分低级错误。6. 处理坐标偏差和叠加错位时的排查顺序底图的坐标基准和你的数据基准如果不一致叠加出来的结果会整体偏移。这个问题的表现是形状对得上整体平移了几百米和坐标顺序错误点完全跑飞有明显区别靠这个特征就能快速区分。排查我一般按这个顺序走从成本最低的开始第一确认数据本身的坐标基准。WGS84、Web MercatorEPSG:3857、其他投影坐标这三类处理方式完全不同。Web Mercator 用的是米制坐标数值通常在几百万量级一眼就能看出来。如果数值在 ±180 和 ±90 内那基本是经纬度。第二确认渲染底图用的是什么基准。不同地图服务商的底图数据基准可能不同如果你的数据是 WGS84底图是另一种基准叠加时就会产生系统性偏移。这时候的处理方式是统一到同一基准再叠加而不是靠肉眼微调偏移量。第三确认投影转换有没有做。Web Mercator 的转换公式是标准的纬度方向用到了对数函数自行实现时容易写错建议直接用成熟库处理。// 经纬度转 Web MercatorEPSG:3857常用在自定义着色器或者手写投影时 function lngLatToMercator(lng, lat) { const x (lng * 20037508.34) / 180; let y Math.log(Math.tan(((90 lat) * Math.PI) / 360)) / (Math.PI / 180); y (y * 20037508.34) / 180; return [x, y]; }第四确认精度截断有没有造成可见偏差。这一条一般不会导致整体偏移但如果数据被截断得过分比如只剩 2 位小数相邻要素之间会出现明显的对齐缝隙看起来像是错位实际是精度问题。这四步走下来绝大多数偏移问题都能定位到具体环节。核心思路就是别凭感觉调偏移量一定要找到是哪一步的基准不一致。7. 几句实在话JSON 和 GeoJSON 的关系说到底就是通用语法和领域方言的关系。写 GeoJSON 的时候脑子里要同时跑两套检查JSON 那一套检查有没有写错括号引号GeoJSON 那一套检查坐标顺序、环方向、类型取值、坐标基准对不对。前者靠工具后者靠习惯。我自己的几条固定做法都是被坑出来的接口返回 GeoJSON 时先在测试环境跑一遍坐标范围检查导入数据库前先跑几何有效性校验数据落盘前先做精度截断并重新算 bbox前端解析失败时一定把文本长度和尾部内容打出来。这几条加起来花不了多少时间但省下来的排查时间非常可观。最后提一个小技巧如果一份 GeoJSON 怎么都渲染不出来先别怀疑渲染代码。把文件拖进 QGIS 看一眼QGIS 能正常显示说明数据没问题问题在前端QGIS 也不显示或者显示了但位置奇怪问题在数据本身。这个分流动作能省掉一半的排查时间我每次都用。
网站建设高端定制企业官网