/* * 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 * as zrUtil from 'zrender/src/core/util'; import BoundingRect from 'zrender/src/core/BoundingRect'; import View from '../View'; import geoSourceManager from './geoSourceManager'; import { GeoJSONRegion, Region } from './Region'; import { GeoResource, NameMap } from './geoTypes'; import GlobalModel from '../../model/Global'; import { ParsedModelFinder, ParsedModelFinderKnown, SINGLE_REFERRING } from '../../util/model'; import GeoModel from './GeoModel'; import { resizeGeoType } from './geoCreator'; const GEO_DEFAULT_PARAMS: { [type in GeoResource['type']]: { aspectScale: number; invertLongitute: boolean; } } = { 'geoJSON': { aspectScale: 0.75, invertLongitute: true }, 'geoSVG': { aspectScale: 1, invertLongitute: false } } as const; class Geo extends View { dimensions = ['lng', 'lat']; type = 'geo'; // map type readonly map: string; readonly resourceType: GeoResource['type']; // Only store specified name coord via `addGeoCoord`. private _nameCoordMap: zrUtil.HashMap; private _regionsMap: zrUtil.HashMap; private _invertLongitute: boolean; readonly regions: Region[]; readonly aspectScale: number; // Injected outside model: GeoModel; resize: resizeGeoType; constructor( name: string, map: string, opt: { // Specify name alias nameMap?: NameMap; nameProperty?: string; aspectScale?: number; } ) { super(name); this.map = map; const source = geoSourceManager.load(map, opt.nameMap, opt.nameProperty); const resource = geoSourceManager.getGeoResource(map); this.resourceType = resource ? resource.type : null; const defaultParmas = GEO_DEFAULT_PARAMS[resource.type]; this._regionsMap = source.regionsMap; this._invertLongitute = defaultParmas.invertLongitute; this.regions = source.regions; this.aspectScale = zrUtil.retrieve2(opt.aspectScale, defaultParmas.aspectScale); const boundingRect = source.boundingRect; this.setBoundingRect(boundingRect.x, boundingRect.y, boundingRect.width, boundingRect.height); } /** * Whether contain the given [lng, lat] coord. */ // Never used yet. // containCoord(coord: number[]) { // const regions = this.regions; // for (let i = 0; i < regions.length; i++) { // const region = regions[i]; // if (region.type === 'geoJSON' && (region as GeoJSONRegion).contain(coord)) { // return true; // } // } // return false; // } protected _transformTo(x: number, y: number, width: number, height: number): void { let rect = this.getBoundingRect(); const invertLongitute = this._invertLongitute; rect = rect.clone(); if (invertLongitute) { // Longitute is inverted rect.y = -rect.y - rect.height; } const rawTransformable = this._rawTransformable; rawTransformable.transform = rect.calculateTransform( new BoundingRect(x, y, width, height) ); const rawParent = rawTransformable.parent; rawTransformable.parent = null; rawTransformable.decomposeTransform(); rawTransformable.parent = rawParent; if (invertLongitute) { rawTransformable.scaleY = -rawTransformable.scaleY; } this._updateTransform(); } getRegion(name: string): Region { return this._regionsMap.get(name); } getRegionByCoord(coord: number[]): Region { const regions = this.regions; for (let i = 0; i < regions.length; i++) { const region = regions[i]; if (region.type === 'geoJSON' && (region as GeoJSONRegion).contain(coord)) { return regions[i]; } } } /** * Add geoCoord for indexing by name */ addGeoCoord(name: string, geoCoord: number[]): void { this._nameCoordMap.set(name, geoCoord); } /** * Get geoCoord by name */ getGeoCoord(name: string): number[] { const region = this._regionsMap.get(name); // calcualte center only on demand. return this._nameCoordMap.get(name) || (region && region.getCenter()); } dataToPoint(data: number[], noRoam?: boolean, out?: number[]): number[] { if (typeof data === 'string') { // Map area name to geoCoord data = this.getGeoCoord(data); } if (data) { return View.prototype.dataToPoint.call(this, data, noRoam, out); } } convertToPixel(ecModel: GlobalModel, finder: ParsedModelFinder, value: number[]): number[] { const coordSys = getCoordSys(finder); return coordSys === this ? coordSys.dataToPoint(value) : null; } convertFromPixel(ecModel: GlobalModel, finder: ParsedModelFinder, pixel: number[]): number[] { const coordSys = getCoordSys(finder); return coordSys === this ? coordSys.pointToData(pixel) : null; } }; zrUtil.mixin(Geo, View); function getCoordSys(finder: ParsedModelFinderKnown): Geo { const geoModel = finder.geoModel as GeoModel; const seriesModel = finder.seriesModel; return geoModel ? geoModel.coordinateSystem : seriesModel ? ( seriesModel.coordinateSystem as Geo // For map series. || ( (seriesModel.getReferringComponents('geo', SINGLE_REFERRING).models[0] || {} ) as GeoModel).coordinateSystem ) : null; } export default Geo;