/*
|
* 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 + ');'))();
|
}
|