|  | /* | 
|  | * 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, hasOwn } from 'zrender/src/core/util'; | 
|  | import parseGeoJson from './parseGeoJson'; | 
|  | // Built-in GEO fixer. | 
|  | import fixNanhai from './fix/nanhai'; | 
|  | import fixTextCoord from './fix/textCoord'; | 
|  | 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 && hasOwn(nameMap, 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); | 
|  | 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 + ');'))(); | 
|  | } |