| /* |
| * 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. |
| */ |
| |
| |
| import { each, isString, createHashMap } from 'zrender/src/core/util'; |
| import parseGeoJson from './parseGeoJson'; |
| // Built-in GEO fixer. |
| import fixNanhai from './fix/nanhai'; |
| import fixTextCoord from './fix/textCoord'; |
| import fixGeoCoord from './fix/geoCoord'; |
| import fixDiaoyuIsland from './fix/diaoyuIsland'; |
| import BoundingRect from 'zrender/src/core/BoundingRect'; |
| import { GeoJSONRegion } from './Region'; |
| import { GeoJSON, GeoJSONCompressed, GeoJSONSourceInput, GeoResource, GeoSpecialAreas, NameMap } from './geoTypes'; |
| |
| |
| const DEFAULT_NAME_PROPERTY = 'name' as const; |
| |
| export class GeoJSONResource implements GeoResource { |
| |
| readonly type = 'geoJSON'; |
| private _geoJSON: GeoJSON | GeoJSONCompressed; |
| private _specialAreas: GeoSpecialAreas; |
| private _mapName: string; |
| |
| private _parsedMap = createHashMap<{ |
| regions: GeoJSONRegion[]; |
| boundingRect: BoundingRect; |
| }, string>(); |
| |
| constructor( |
| mapName: string, |
| geoJSON: GeoJSONSourceInput, |
| specialAreas: GeoSpecialAreas |
| ) { |
| this._mapName = mapName; |
| this._specialAreas = specialAreas; |
| |
| // PENDING: delay the parse to the first usage to rapid up the FMP? |
| this._geoJSON = parseInput(geoJSON); |
| } |
| |
| /** |
| * @param nameMap can be null/undefined |
| * @param nameProperty can be null/undefined |
| */ |
| load(nameMap: NameMap, nameProperty: string) { |
| |
| nameProperty = nameProperty || DEFAULT_NAME_PROPERTY; |
| |
| let parsed = this._parsedMap.get(nameProperty); |
| if (!parsed) { |
| const rawRegions = this._parseToRegions(nameProperty); |
| parsed = this._parsedMap.set(nameProperty, { |
| regions: rawRegions, |
| boundingRect: calculateBoundingRect(rawRegions) |
| }); |
| } |
| |
| const regionsMap = createHashMap<GeoJSONRegion>(); |
| |
| const finalRegions: GeoJSONRegion[] = []; |
| each(parsed.regions, function (region) { |
| let regionName = region.name; |
| |
| // Try use the alias in geoNameMap |
| if (nameMap && nameMap.hasOwnProperty(regionName)) { |
| region = region.cloneShallow(regionName = nameMap[regionName]); |
| } |
| |
| finalRegions.push(region); |
| regionsMap.set(regionName, region); |
| }); |
| |
| return { |
| regions: finalRegions, |
| boundingRect: parsed.boundingRect || new BoundingRect(0, 0, 0, 0), |
| regionsMap: regionsMap |
| }; |
| } |
| |
| private _parseToRegions(nameProperty: string): GeoJSONRegion[] { |
| const mapName = this._mapName; |
| const geoJSON = this._geoJSON; |
| let rawRegions; |
| |
| // https://jsperf.com/try-catch-performance-overhead |
| try { |
| rawRegions = geoJSON ? parseGeoJson(geoJSON, nameProperty) : []; |
| } |
| catch (e) { |
| throw new Error('Invalid geoJson format\n' + e.message); |
| } |
| |
| fixNanhai(mapName, rawRegions); |
| |
| each(rawRegions, function (region) { |
| const regionName = region.name; |
| |
| fixTextCoord(mapName, region); |
| fixGeoCoord(mapName, region); |
| fixDiaoyuIsland(mapName, region); |
| |
| // Some area like Alaska in USA map needs to be tansformed |
| // to look better |
| const specialArea = this._specialAreas && this._specialAreas[regionName]; |
| if (specialArea) { |
| region.transformTo( |
| specialArea.left, specialArea.top, specialArea.width, specialArea.height |
| ); |
| } |
| }, this); |
| |
| return rawRegions; |
| } |
| |
| /** |
| * Only for exporting to users. |
| * **MUST NOT** used internally. |
| */ |
| getMapForUser(): { |
| // backward compat. |
| geoJson: GeoJSON | GeoJSONCompressed; |
| geoJSON: GeoJSON | GeoJSONCompressed; |
| specialAreas: GeoSpecialAreas; |
| } { |
| return { |
| // For backward compatibility, use geoJson |
| // PENDING: it has been returning them without clone. |
| // do we need to avoid outsite modification? |
| geoJson: this._geoJSON, |
| geoJSON: this._geoJSON, |
| specialAreas: this._specialAreas |
| }; |
| } |
| |
| } |
| |
| function calculateBoundingRect(regions: GeoJSONRegion[]): BoundingRect { |
| let rect; |
| for (let i = 0; i < regions.length; i++) { |
| const regionRect = regions[i].getBoundingRect(); |
| rect = rect || regionRect.clone(); |
| rect.union(regionRect); |
| } |
| return rect; |
| } |
| |
| function parseInput(source: GeoJSONSourceInput): GeoJSON | GeoJSONCompressed { |
| return !isString(source) |
| ? source |
| : (typeof JSON !== 'undefined' && JSON.parse) |
| ? JSON.parse(source) |
| : (new Function('return (' + source + ');'))(); |
| } |