| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| /** | |
| * Parse and decode geo json | |
| */ | |
| import * as zrUtil from 'zrender/src/core/util'; | |
| import { GeoJSONLineStringGeometry, GeoJSONPolygonGeometry, GeoJSONRegion } from './Region'; | |
| import { GeoJSONCompressed, GeoJSON } from './geoTypes'; | |
| function decode(json: GeoJSONCompressed | GeoJSON): GeoJSON { | |
| if (!(json as GeoJSONCompressed).UTF8Encoding) { | |
| return json as GeoJSON; | |
| } | |
| const jsonCompressed = json as GeoJSONCompressed; | |
| let encodeScale = jsonCompressed.UTF8Scale; | |
| if (encodeScale == null) { | |
| encodeScale = 1024; | |
| } | |
| const features = jsonCompressed.features; | |
| zrUtil.each(features, feature => { | |
| const geometry = feature.geometry; | |
| const encodeOffsets = geometry.encodeOffsets; | |
| const coordinates = geometry.coordinates; | |
| // Geometry may be appeded manually in the script after json loaded. | |
| // In this case this geometry is usually not encoded. | |
| if (!encodeOffsets) { | |
| return; | |
| } | |
| switch (geometry.type) { | |
| case 'LineString': | |
| (geometry as any).coordinates = | |
| decodeRing(coordinates as string, encodeOffsets as number[], encodeScale); | |
| break; | |
| case 'Polygon': | |
| decodeRings(coordinates as string[], encodeOffsets as number[][], encodeScale); | |
| break; | |
| case 'MultiLineString': | |
| decodeRings(coordinates as string[], encodeOffsets as number[][], encodeScale); | |
| break; | |
| case 'MultiPolygon': | |
| zrUtil.each( | |
| coordinates as string[][], | |
| (rings, idx) => decodeRings(rings, (encodeOffsets as number[][][])[idx], encodeScale) | |
| ); | |
| } | |
| }); | |
| // Has been decoded | |
| jsonCompressed.UTF8Encoding = false; | |
| return jsonCompressed as unknown as GeoJSON; | |
| } | |
| function decodeRings( | |
| rings: string[], | |
| encodeOffsets: number[][], | |
| encodeScale: number | |
| ) { | |
| for (let c = 0; c < rings.length; c++) { | |
| rings[c] = decodeRing( | |
| rings[c], | |
| encodeOffsets[c], | |
| encodeScale | |
| ) as any; | |
| } | |
| } | |
| function decodeRing( | |
| coordinate: string, | |
| encodeOffsets: number[], | |
| encodeScale: number | |
| ): number[][] { | |
| const result = []; | |
| let prevX = encodeOffsets[0]; | |
| let prevY = encodeOffsets[1]; | |
| for (let i = 0; i < coordinate.length; i += 2) { | |
| let x = coordinate.charCodeAt(i) - 64; | |
| let y = coordinate.charCodeAt(i + 1) - 64; | |
| // ZigZag decoding | |
| x = (x >> 1) ^ (-(x & 1)); | |
| y = (y >> 1) ^ (-(y & 1)); | |
| // Delta deocding | |
| x += prevX; | |
| y += prevY; | |
| prevX = x; | |
| prevY = y; | |
| // Dequantize | |
| result.push([x / encodeScale, y / encodeScale]); | |
| } | |
| return result; | |
| } | |
| export default function parseGeoJSON(geoJson: GeoJSON | GeoJSONCompressed, nameProperty: string): GeoJSONRegion[] { | |
| geoJson = decode(geoJson); | |
| return zrUtil.map(zrUtil.filter(geoJson.features, function (featureObj) { | |
| // Output of mapshaper may have geometry null | |
| return featureObj.geometry | |
| && featureObj.properties | |
| && featureObj.geometry.coordinates.length > 0; | |
| }), function (featureObj) { | |
| const properties = featureObj.properties; | |
| const geo = featureObj.geometry; | |
| const geometries = [] as GeoJSONRegion['geometries']; | |
| switch (geo.type) { | |
| case 'Polygon': | |
| const coordinates = geo.coordinates; | |
| // According to the GeoJSON specification. | |
| // First must be exterior, and the rest are all interior(holes). | |
| geometries.push(new GeoJSONPolygonGeometry(coordinates[0], coordinates.slice(1))); | |
| break; | |
| case 'MultiPolygon': | |
| zrUtil.each(geo.coordinates, function (item) { | |
| if (item[0]) { | |
| geometries.push(new GeoJSONPolygonGeometry(item[0], item.slice(1))); | |
| } | |
| }); | |
| break; | |
| case 'LineString': | |
| geometries.push(new GeoJSONLineStringGeometry([geo.coordinates])); | |
| break; | |
| case 'MultiLineString': | |
| geometries.push(new GeoJSONLineStringGeometry(geo.coordinates)); | |
| } | |
| const region = new GeoJSONRegion( | |
| properties[nameProperty || 'name'], | |
| geometries, | |
| properties.cp | |
| ); | |
| region.properties = properties; | |
| return region; | |
| }); | |
| } |