You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by su...@apache.org on 2021/03/09 17:58:18 UTC

[echarts] branch fix/geo-svg created (now 30c861c)

This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a change to branch fix/geo-svg
in repository https://gitbox.apache.org/repos/asf/echarts.git.


      at 30c861c  feature: [geo] (1) "geoselectchanged" event: add param: allSelected: { geoIndex: number, name: string[] }[] (2) geoSVG resource support: + label + emphasis/select/blur state + "geoselectchanged" event (on geo component) + "selectchanged" event (on map series) (3) some refactor to make those happen: + The original `Region` is migrated to `GeoJSONRegion`. And make `Region` as the base class of `GeoJSONRegion` and `GeoSVGRegion`. + Modify the constructor of `Geo`.

This branch includes the following new commits:

     new cc7001d  fix: tweak
     new 6a22cc6  fix: [geo] clean code and add comment and make interface better.
     new e9af050  fix: [geo] (1) refactor of geoJSON & SVG source loader and management. (2) remove the support of "load multiple geoJSON or SVG in one map name". This feature is never documented and published. And this feature have a inherent problem that it's not easy to normalize the unit among the different SVG. After this commit, one map name can only have one geoJSON or one SVG.
     new 6b5a0e5  fix: [geo] (1) label scale implementation change: user afterUpdate rather than / parentScale. (2) some refactor and clean code.
     new ca58174  feature: [geo] support geo svg named elements have the same behavior as regions of geoJSON
     new 30c861c  feature: [geo] (1) "geoselectchanged" event: add param: allSelected: { geoIndex: number, name: string[] }[] (2) geoSVG resource support: + label + emphasis/select/blur state + "geoselectchanged" event (on geo component) + "selectchanged" event (on map series) (3) some refactor to make those happen: + The original `Region` is migrated to `GeoJSONRegion`. And make `Region` as the base class of `GeoJSONRegion` and `GeoSVGRegion`. + Modify the constructor of `Geo`.

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org


[echarts] 06/06: feature: [geo] (1) "geoselectchanged" event: add param: allSelected: { geoIndex: number, name: string[] }[] (2) geoSVG resource support: + label + emphasis/select/blur state + "geoselectchanged" event (on geo component) + "selectchanged" event (on map series) (3) some refactor to make those happen: + The original `Region` is migrated to `GeoJSONRegion`. And make `Region` as the base class of `GeoJSONRegion` and `GeoSVGRegion`. + Modify the constructor of `Geo`.

Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a commit to branch fix/geo-svg
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit 30c861cc88d0b9831256d44f4a0404c05e825710
Author: 100pah <su...@gmail.com>
AuthorDate: Wed Mar 10 01:57:25 2021 +0800

    feature: [geo]
    (1) "geoselectchanged" event: add param: allSelected: { geoIndex: number, name: string[] }[]
    (2) geoSVG resource support:
    + label
    + emphasis/select/blur state
    + "geoselectchanged" event (on geo component)
    + "selectchanged" event (on map series)
    (3) some refactor to make those happen:
    + The original `Region` is migrated to `GeoJSONRegion`. And make `Region` as the base class of `GeoJSONRegion` and `GeoSVGRegion`.
    + Modify the constructor of `Geo`.
---
 src/chart/map/MapSeries.ts        |   7 ++-
 src/chart/map/mapSymbolLayout.ts  |   4 +-
 src/component/geo/install.ts      |  16 ++++-
 src/component/helper/MapDraw.ts   |  32 ++++++----
 src/coord/geo/Geo.ts              |  55 +++++++++++-----
 src/coord/geo/GeoJSONResource.ts  |  16 ++---
 src/coord/geo/GeoModel.ts         |   4 +-
 src/coord/geo/GeoSVGResource.ts   |  96 +++++++++++++++++++---------
 src/coord/geo/Region.ts           | 112 +++++++++++++++++++++++++++++----
 src/coord/geo/fix/diaoyuIsland.ts |   6 +-
 src/coord/geo/fix/geoCoord.ts     |  16 ++---
 src/coord/geo/fix/nanhai.ts       |   6 +-
 src/coord/geo/fix/textCoord.ts    |  10 +--
 src/coord/geo/geoCreator.ts       |  27 ++++----
 src/coord/geo/geoSourceManager.ts |   2 +-
 src/coord/geo/geoTypes.ts         |  19 +++---
 src/coord/geo/parseGeoJson.ts     |   8 +--
 test/geo-svg.html                 | 128 +++++++++++++++++++++++++++++++++-----
 18 files changed, 417 insertions(+), 147 deletions(-)

diff --git a/src/chart/map/MapSeries.ts b/src/chart/map/MapSeries.ts
index 2f0f3d8..1532dca 100644
--- a/src/chart/map/MapSeries.ts
+++ b/src/chart/map/MapSeries.ts
@@ -212,7 +212,7 @@ class MapSeries extends SeriesModel<MapSeriesOption> {
             const geo = this.coordinateSystem;
             const region = geo.getRegion(name);
 
-            return region && geo.dataToPoint(region.center);
+            return region && geo.dataToPoint(region.getCenter());
         }
     };
 
@@ -251,7 +251,10 @@ class MapSeries extends SeriesModel<MapSeriesOption> {
 
         // Aspect is width / height. Inited to be geoJson bbox aspect
         // This parameter is used for scale this aspect
-        aspectScale: 0.75,
+        // Default value:
+        // for geoSVG source: 1,
+        // for geoJSON source: 0.75.
+        aspectScale: null,
 
         ///// Layout with center and size
         // If you wan't to put map in a fixed size box with right aspect ratio
diff --git a/src/chart/map/mapSymbolLayout.ts b/src/chart/map/mapSymbolLayout.ts
index 65e8d31..498788f 100644
--- a/src/chart/map/mapSymbolLayout.ts
+++ b/src/chart/map/mapSymbolLayout.ts
@@ -22,6 +22,7 @@ import * as zrUtil from 'zrender/src/core/util';
 import GlobalModel from '../../model/Global';
 import MapSeries from './MapSeries';
 import { Dictionary } from '../../util/types';
+import { GeoJSONRegion } from '../../coord/geo/Region';
 
 export default function mapSymbolLayout(ecModel: GlobalModel) {
 
@@ -38,6 +39,7 @@ export default function mapSymbolLayout(ecModel: GlobalModel) {
         zrUtil.each(mapSeries.seriesGroup, function (subMapSeries) {
             const geo = subMapSeries.coordinateSystem;
             const data = subMapSeries.originalData;
+
             if (subMapSeries.get('showLegendSymbol') && ecModel.getComponent('legend')) {
                 data.each(data.mapDimension('value'), function (value, idx) {
                     const name = data.getName(idx);
@@ -52,7 +54,7 @@ export default function mapSymbolLayout(ecModel: GlobalModel) {
 
                     const offset = mapSymbolOffsets[name] || 0;
 
-                    const point = geo.dataToPoint(region.center);
+                    const point = geo.dataToPoint(region.getCenter());
 
                     mapSymbolOffsets[name] = offset + 1;
 
diff --git a/src/component/geo/install.ts b/src/component/geo/install.ts
index cb0de29..0db971a 100644
--- a/src/component/geo/install.ts
+++ b/src/component/geo/install.ts
@@ -42,20 +42,34 @@ export function install(registers: EChartsExtensionInstallRegisters) {
         actionInfo.update = 'geo:updateSelectStatus';
         registers.registerAction(actionInfo, function (payload, ecModel) {
             const selected = {} as {[regionName: string]: boolean};
+            const allSelected = [] as ({ name: string[], geoIndex: number })[];
 
             ecModel.eachComponent(
                 { mainType: 'geo', query: payload},
                 function (geoModel: GeoModel) {
                     geoModel[method](payload.name);
                     const geo = geoModel.coordinateSystem;
+
                     each(geo.regions, function (region) {
                         selected[region.name] = geoModel.isSelected(region.name) || false;
                     });
+
+                    // Notice: there might be duplicated name in different regions.
+                    const names = [] as string[];
+                    each(selected, function (v, name) {
+                        selected[name] && names.push(name);
+                    });
+                    allSelected.push({
+                        geoIndex: geoModel.componentIndex,
+                        // Use singular, the same naming convention as the event `selectchanged`.
+                        name: names
+                    });
                 }
             );
 
             return {
                 selected: selected,
+                allSelected: allSelected,
                 name: payload.name
             };
         });
@@ -118,5 +132,5 @@ export function install(registers: EChartsExtensionInstallRegisters) {
                 }
             }
         );
-    })
+    });
 }
\ No newline at end of file
diff --git a/src/component/helper/MapDraw.ts b/src/component/helper/MapDraw.ts
index a7176bf..15634f2 100644
--- a/src/component/helper/MapDraw.ts
+++ b/src/component/helper/MapDraw.ts
@@ -42,6 +42,7 @@ import { GeoSVGResource } from '../../coord/geo/GeoSVGResource';
 import Displayable from 'zrender/src/graphic/Displayable';
 import Element, { ElementTextConfig } from 'zrender/src/Element';
 import List from '../../data/List';
+import { GeoJSONRegion } from '../../coord/geo/Region';
 
 
 interface RegionsGroup extends graphic.Group {
@@ -91,13 +92,13 @@ class MapDraw {
      */
     private _mouseDownFlag: boolean;
 
-    private _svgMapName: string;
-
     private _regionsGroup: RegionsGroup;
 
+    private _svgMapName: string;
+
     private _svgGroup: graphic.Group;
 
-    private _svgNamedElements: Displayable[];
+    private _svgRegionElements: Displayable[];
 
 
     constructor(api: ExtensionAPI) {
@@ -167,8 +168,12 @@ class MapDraw {
             transformInfoRaw
         };
 
-        this._buildGeoJSON(viewBuildCtx);
-        this._buildSVG(viewBuildCtx);
+        if (geo.resourceType === 'geoJSON') {
+            this._buildGeoJSON(viewBuildCtx);
+        }
+        else if (geo.resourceType === 'geoSVG') {
+            this._buildSVG(viewBuildCtx);
+        }
 
         this._updateController(mapOrGeoModel, ecModel, api);
 
@@ -190,7 +195,7 @@ class MapDraw {
         regionsGroup.removeAll();
 
         // Only when the resource is GeoJSON, there is `geo.regions`.
-        zrUtil.each(viewBuildCtx.geo.regions, function (region) {
+        zrUtil.each(viewBuildCtx.geo.regions, function (region: GeoJSONRegion) {
 
             // Consider in GeoJson properties.name may be duplicated, for example,
             // there is multiple region named "United Kindom" or "France" (so many
@@ -238,7 +243,7 @@ class MapDraw {
                 }
             });
 
-            const centerPt = transformPoint(region.center);
+            const centerPt = transformPoint(region.getCenter());
 
             this._resetSingleRegionGraphic(
                 viewBuildCtx, compoundPath, regionGroup, region.name, centerPt, null
@@ -263,9 +268,9 @@ class MapDraw {
             this._useSVG(mapName);
         }
 
-        zrUtil.each(this._svgNamedElements, function (namedElement) {
+        zrUtil.each(this._svgRegionElements, function (el: Displayable) {
             this._resetSingleRegionGraphic(
-                viewBuildCtx, namedElement, namedElement, namedElement.name, [0, 0], 'inside'
+                viewBuildCtx, el, el, el.name, [0, 0], 'inside'
             );
         }, this);
     }
@@ -430,10 +435,10 @@ class MapDraw {
 
     private _useSVG(mapName: string): void {
         const resource = geoSourceManager.getGeoResource(mapName);
-        if (resource && resource.type === 'svg') {
+        if (resource && resource.type === 'geoSVG') {
             const svgGraphic = (resource as GeoSVGResource).useGraphic(this.uid);
             this._svgGroup.add(svgGraphic.root);
-            this._svgNamedElements = svgGraphic.namedElements;
+            this._svgRegionElements = svgGraphic.regionElements;
             this._svgMapName = mapName;
         }
     }
@@ -444,11 +449,11 @@ class MapDraw {
             return;
         }
         const resource = geoSourceManager.getGeoResource(mapName);
-        if (resource && resource.type === 'svg') {
+        if (resource && resource.type === 'geoSVG') {
             (resource as GeoSVGResource).freeGraphic(this.uid);
         }
+        this._svgRegionElements = null;
         this._svgGroup.removeAll();
-        this._svgNamedElements = null;
         this._svgMapName = null;
     }
 
@@ -516,6 +521,7 @@ class MapDraw {
         const mapDraw = this;
 
         regionsGroup.off('mousedown');
+        regionsGroup.off('click');
 
         // @ts-ignore FIXME:TS resolve type conflict
         if (mapOrGeoModel.get('selectedMode')) {
diff --git a/src/coord/geo/Geo.ts b/src/coord/geo/Geo.ts
index 516a971..1f29cc5 100644
--- a/src/coord/geo/Geo.ts
+++ b/src/coord/geo/Geo.ts
@@ -21,13 +21,29 @@ import * as zrUtil from 'zrender/src/core/util';
 import BoundingRect from 'zrender/src/core/BoundingRect';
 import View from '../View';
 import geoSourceManager from './geoSourceManager';
-import Region from './Region';
-import { NameMap } from './geoTypes';
+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 {
 
@@ -37,35 +53,42 @@ class Geo extends View {
 
     // map type
     readonly map: string;
+    readonly resourceType: GeoResource['type'];
 
     private _nameCoordMap: zrUtil.HashMap<number[]>;
     private _regionsMap: zrUtil.HashMap<Region>;
     private _invertLongitute: boolean;
     readonly regions: Region[];
+    readonly aspectScale: number;
 
     // Injected outside
-    aspectScale: number;
     model: GeoModel;
     resize: resizeGeoType;
 
-    /**
-     * For backward compatibility, the orginal interface:
-     * `name, map, geoJson, specialAreas, nameMap` is kept.
-     *
-     * @param map Map type Specify the positioned areas by left, top, width, height.
-     * @param [nameMap] Specify name alias
-     */
-    constructor(name: string, map: string, nameMap?: NameMap, invertLongitute?: boolean) {
+    constructor(
+        name: string,
+        map: string,
+        opt: {
+            // Specify name alias
+            nameMap?: NameMap;
+            aspectScale?: number;
+        }
+    ) {
         super(name);
 
         this.map = map;
 
-        const source = geoSourceManager.load(map, nameMap);
+        const source = geoSourceManager.load(map, opt.nameMap);
+        const resource = geoSourceManager.getGeoResource(map);
+        this.resourceType = resource ? resource.type : null;
+
+        const defaultParmas = GEO_DEFAULT_PARAMS[resource.type];
 
         this._nameCoordMap = source.nameCoordMap;
         this._regionsMap = source.regionsMap;
-        this._invertLongitute = invertLongitute == null ? true : invertLongitute;
+        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);
@@ -77,7 +100,8 @@ class Geo extends View {
     containCoord(coord: number[]) {
         const regions = this.regions;
         for (let i = 0; i < regions.length; i++) {
-            if (regions[i].contain(coord)) {
+            const region = regions[i];
+            if (region.type === 'geoJSON' && (region as GeoJSONRegion).contain(coord)) {
                 return true;
             }
         }
@@ -120,7 +144,8 @@ class Geo extends View {
     getRegionByCoord(coord: number[]): Region {
         const regions = this.regions;
         for (let i = 0; i < regions.length; i++) {
-            if (regions[i].contain(coord)) {
+            const region = regions[i];
+            if (region.type === 'geoJSON' && (region as GeoJSONRegion).contain(coord)) {
                 return regions[i];
             }
         }
diff --git a/src/coord/geo/GeoJSONResource.ts b/src/coord/geo/GeoJSONResource.ts
index 6189322..e5aea61 100644
--- a/src/coord/geo/GeoJSONResource.ts
+++ b/src/coord/geo/GeoJSONResource.ts
@@ -26,7 +26,7 @@ import fixTextCoord from './fix/textCoord';
 import fixGeoCoord from './fix/geoCoord';
 import fixDiaoyuIsland from './fix/diaoyuIsland';
 import BoundingRect from 'zrender/src/core/BoundingRect';
-import Region from './Region';
+import { GeoJSONRegion } from './Region';
 import { GeoJSON, GeoJSONCompressed, GeoJSONSourceInput, GeoResource, GeoSpecialAreas, NameMap } from './geoTypes';
 
 
@@ -38,7 +38,7 @@ export class GeoJSONResource implements GeoResource {
     private _mapName: string;
 
     private _parsed: {
-        regions: Region[];
+        regions: GeoJSONRegion[];
         boundingRect: BoundingRect;
     };
 
@@ -65,10 +65,10 @@ export class GeoJSONResource implements GeoResource {
             };
         }
 
-        const regionsMap = createHashMap<Region>();
-        const nameCoordMap = createHashMap<Region['center']>();
+        const regionsMap = createHashMap<GeoJSONRegion>();
+        const nameCoordMap = createHashMap<ReturnType<GeoJSONRegion['getCenter']>>();
 
-        const finalRegions: Region[] = [];
+        const finalRegions: GeoJSONRegion[] = [];
         each(parsed.regions, function (region) {
             let regionName = region.name;
 
@@ -79,7 +79,7 @@ export class GeoJSONResource implements GeoResource {
 
             finalRegions.push(region);
             regionsMap.set(regionName, region);
-            nameCoordMap.set(regionName, region.center);
+            nameCoordMap.set(regionName, region.getCenter());
         });
 
         return {
@@ -90,7 +90,7 @@ export class GeoJSONResource implements GeoResource {
         };
     }
 
-    private _parseToRegions(nameProperty: string): Region[] {
+    private _parseToRegions(nameProperty: string): GeoJSONRegion[] {
         const mapName = this._mapName;
         const geoJSON = this._geoJSON;
         let rawRegions;
@@ -147,7 +147,7 @@ export class GeoJSONResource implements GeoResource {
 
 }
 
-function calculateBoundingRect(regions: Region[]): BoundingRect {
+function calculateBoundingRect(regions: GeoJSONRegion[]): BoundingRect {
     let rect;
     for (let i = 0; i < regions.length; i++) {
         const regionRect = regions[i].getBoundingRect();
diff --git a/src/coord/geo/GeoModel.ts b/src/coord/geo/GeoModel.ts
index 78054c6..7095c6f 100644
--- a/src/coord/geo/GeoModel.ts
+++ b/src/coord/geo/GeoModel.ts
@@ -129,7 +129,7 @@ class GeoModel extends ComponentModel<GeoOption> {
         top: 'center',
 
         // Default value:
-        // for SVG source: 1,
+        // for geoSVG source: 1,
         // for geoJSON source: 0.75.
         aspectScale: null,
 
@@ -287,8 +287,6 @@ class GeoModel extends ComponentModel<GeoOption> {
         return !!(selectedMap && selectedMap[name]);
     }
 
-    private _initSelectedMapFromData() {
-    }
 }
 
 export default GeoModel;
diff --git a/src/coord/geo/GeoSVGResource.ts b/src/coord/geo/GeoSVGResource.ts
index 43c18fa..0099161 100644
--- a/src/coord/geo/GeoSVGResource.ts
+++ b/src/coord/geo/GeoSVGResource.ts
@@ -22,25 +22,35 @@ import Group from 'zrender/src/graphic/Group';
 import Rect from 'zrender/src/graphic/shape/Rect';
 import {assert, createHashMap, HashMap} from 'zrender/src/core/util';
 import BoundingRect from 'zrender/src/core/BoundingRect';
-import { GeoResource, GeoSVGSourceInput } from './geoTypes';
+import { GeoResource, GeoSVGGraphicRoot, GeoSVGSourceInput, NameMap } from './geoTypes';
 import { parseXML } from 'zrender/src/tool/parseXML';
 import Displayable from 'zrender/src/graphic/Displayable';
+import { GeoSVGRegion } from './Region';
 
-export interface GeoSVGGraphic {
+interface GeoSVGGraphicRecord {
     root: Group;
-    namedElements: Displayable[];
+    boundingRect: BoundingRect;
+    regionElements: Displayable[];
 }
 
 export class GeoSVGResource implements GeoResource {
 
-    readonly type = 'svg';
+    readonly type = 'geoSVG';
     private _mapName: string;
     private _parsedXML: SVGElement;
-    private _rootForRect: GeoSVGGraphic;
+
+    private _firstGraphic: GeoSVGGraphicRecord;
     private _boundingRect: BoundingRect;
-    // key: hostKey, value: root
-    private _usedRootMap: HashMap<GeoSVGGraphic> = createHashMap();
-    private _freedRoots: GeoSVGGraphic[] = [];
+    private _regions: GeoSVGRegion[] = [];
+    // Key: region.name
+    private _regionsMap: HashMap<GeoSVGRegion> = createHashMap<GeoSVGRegion>();
+    // Key: region.name
+    private _nameCoordMap: HashMap<number[]> = createHashMap<number[]>();
+
+    // All used graphics. key: hostKey, value: root
+    private _usedGraphicMap: HashMap<GeoSVGGraphicRecord> = createHashMap();
+    // All unused graphics.
+    private _freedGraphics: GeoSVGGraphicRecord[] = [];
 
     constructor(
         mapName: string,
@@ -57,22 +67,47 @@ export class GeoSVGResource implements GeoResource {
         this._parsedXML = parseXML(svg);
     }
 
-    load(): { boundingRect: BoundingRect } {
+    load(nameMap: NameMap, nameProperty: string) {
         // In the "load" stage, graphic need to be built to
         // get boundingRect for geo coordinate system.
-        const rootForRect = this._rootForRect;
-        if (rootForRect) {
-            return { boundingRect: this._boundingRect };
-        }
+        let firstGraphic = this._firstGraphic;
+
+        // Create the return data structure only when first graphic created.
+        // Because they will be used in geo coordinate system update stage,
+        // and `regions` will be mounted at `geo` coordinate system,
+        // in which there is no "view" info, so that it should better not to
+        // make references to graphic elements.
+        if (!firstGraphic) {
+            firstGraphic = this._firstGraphic = buildGraphic(this._parsedXML);
+
+            this._freedGraphics.push(firstGraphic);
 
-        const graphic = buildGraphic(this._parsedXML);
+            this._boundingRect = this._firstGraphic.boundingRect.clone();
 
-        this._rootForRect = graphic;
-        this._boundingRect = graphic.boundingRect;
+            // Create resions only for the first graphic, see coments below.
+            const regionElements = firstGraphic.regionElements;
+            for (let i = 0; i < regionElements.length; i++) {
+                const el = regionElements[i];
 
-        this._freedRoots.push(graphic);
+                // Try use the alias in geoNameMap
+                let regionName = el.name;
+                if (nameMap && nameMap.hasOwnProperty(regionName)) {
+                    regionName = nameMap[regionName];
+                }
 
-        return { boundingRect: graphic.boundingRect };
+                const region = new GeoSVGRegion(regionName, el);
+
+                this._regions.push(region);
+                this._regionsMap.set(regionName, region);
+            }
+        }
+
+        return {
+            boundingRect: this._boundingRect,
+            regions: this._regions,
+            regionsMap: this._regionsMap,
+            nameCoordMap: this._nameCoordMap
+        };
     }
 
     // Consider:
@@ -83,26 +118,28 @@ export class GeoSVGResource implements GeoResource {
     //     and it is called without view info.
     // So we maintain graphic elements in this module, and enables `view` to use/return these
     // graphics from/to the pool with it's uid.
-    useGraphic(hostKey: string): GeoSVGGraphic {
-        const usedRootMap = this._usedRootMap;
+    useGraphic(hostKey: string): GeoSVGGraphicRecord {
+        const usedRootMap = this._usedGraphicMap;
 
         let svgGraphic = usedRootMap.get(hostKey);
         if (svgGraphic) {
             return svgGraphic;
         }
 
-        svgGraphic = this._freedRoots.pop() || buildGraphic(this._parsedXML, this._boundingRect);
+        svgGraphic = this._freedGraphics.pop()
+            // use the first boundingRect to avoid duplicated boundingRect calculation.
+            || buildGraphic(this._parsedXML, this._boundingRect);
 
         return usedRootMap.set(hostKey, svgGraphic);
     }
 
     freeGraphic(hostKey: string): void {
-        const usedRootMap = this._usedRootMap;
+        const usedRootMap = this._usedGraphicMap;
 
         const svgGraphic = usedRootMap.get(hostKey);
         if (svgGraphic) {
             usedRootMap.removeKey(hostKey);
-            this._freedRoots.push(svgGraphic);
+            this._freedGraphics.push(svgGraphic);
         }
     }
 
@@ -111,12 +148,10 @@ export class GeoSVGResource implements GeoResource {
 
 function buildGraphic(
     svgXML: SVGElement,
+    // If input boundingRect, avoid boundingRect calculation,
+    // which might be time-consuming.
     boundingRect?: BoundingRect
-): {
-    root: Group;
-    boundingRect: BoundingRect;
-    namedElements: Displayable[]
-} {
+): GeoSVGGraphicRecord {
     let result;
     let root;
 
@@ -151,6 +186,7 @@ function buildGraphic(
         }
     }
 
+    // Note: we keep the covenant that the root has no transform.
     if (viewBoxRect) {
         const viewBoxTransform = makeViewBoxTransform(viewBoxRect, boundingRect.width, boundingRect.height);
         const elRoot = root;
@@ -165,9 +201,11 @@ function buildGraphic(
         shape: boundingRect.plain()
     }));
 
+    (root as GeoSVGGraphicRoot).isGeoSVGGraphicRoot = true;
+
     return {
         root: root,
         boundingRect: boundingRect,
-        namedElements: result.namedElements
+        regionElements: result.namedElements
     };
 }
diff --git a/src/coord/geo/Region.ts b/src/coord/geo/Region.ts
index 36a07c7..df482e3 100644
--- a/src/coord/geo/Region.ts
+++ b/src/coord/geo/Region.ts
@@ -22,10 +22,38 @@ import BoundingRect from 'zrender/src/core/BoundingRect';
 import * as bbox from 'zrender/src/core/bbox';
 import * as vec2 from 'zrender/src/core/vector';
 import * as polygonContain from 'zrender/src/contain/polygon';
-import { GeoJSON } from './geoTypes';
+import { GeoJSON, GeoSVGGraphicRoot } from './geoTypes';
+import * as matrix from 'zrender/src/core/matrix';
+import Element from 'zrender/src/Element';
 
+const TMP_TRANSFORM = [] as number[];
 
-class Region {
+export class Region {
+
+    readonly name: string;
+    readonly type: 'geoJSON' | 'geoSVG';
+
+    constructor(
+        name: string
+    ) {
+        this.name = name;
+    }
+
+    /**
+     * Get center point in data unit. That is,
+     * for GeoJSONRegion, the unit is lat/lng,
+     * for GeoSVGRegion, the unit is pixel but based on root.
+     */
+    getCenter(): number[] {
+        return;
+    }
+
+}
+
+
+export class GeoJSONRegion extends Region {
+
+    readonly type = 'geoJSON';
 
     readonly geometries: {
         type: 'polygon'; // FIXME:TS Is there other types?
@@ -33,9 +61,7 @@ class Region {
         interiors?: number[][][];
     }[];
 
-    readonly name: string;
-
-    center: number[];
+    private _center: number[];
 
     // Injected outside.
     properties: GeoJSON['features'][0]['properties'];
@@ -45,10 +71,11 @@ class Region {
 
     constructor(
         name: string,
-        geometries: Region['geometries'],
+        geometries: GeoJSONRegion['geometries'],
         cp: GeoJSON['features'][0]['properties']['cp']
     ) {
-        this.name = name;
+        super(name);
+
         this.geometries = geometries;
 
         if (!cp) {
@@ -61,7 +88,7 @@ class Region {
         else {
             cp = [cp[0], cp[1]];
         }
-        this.center = cp;
+        this._center = cp;
     }
 
     getBoundingRect(): BoundingRect {
@@ -155,20 +182,81 @@ class Region {
         rect = this._rect;
         rect.copy(target);
         // Update center
-        this.center = [
+        this._center = [
             rect.x + rect.width / 2,
             rect.y + rect.height / 2
         ];
     }
 
-    cloneShallow(name: string): Region {
+    cloneShallow(name: string): GeoJSONRegion {
         name == null && (name = this.name);
-        const newRegion = new Region(name, this.geometries, this.center);
+        const newRegion = new GeoJSONRegion(name, this.geometries, this._center);
         newRegion._rect = this._rect;
         newRegion.transformTo = null; // Simply avoid to be called.
         return newRegion;
     }
 
+    getCenter() {
+        return this._center;
+    }
+
+    setCenter(center: number[]) {
+        this._center = center;
+    }
+
+}
+
+export class GeoSVGRegion extends Region {
+
+    readonly type = 'geoSVG';
+
+    private _center: number[];
+
+    // Can only be used to calculate, but not be modified.
+    // Becuase this el may not belongs to this view,
+    // but been displaying on some other view.
+    private _elOnlyForCalculate: Element;
+
+    constructor(
+        name: string,
+        elOnlyForCalculate: Element
+    ) {
+        super(name);
+        this._elOnlyForCalculate = elOnlyForCalculate;
+    }
+
+    getCenter() {
+        let center = this._center;
+        if (!center) {
+            // In most cases there are no need to calculate this center.
+            // So calculate only when called.
+            center = this._center = this._calculateCenter();
+        }
+        return center;
+    }
+
+    private _calculateCenter(): number[] {
+        const el = this._elOnlyForCalculate;
+        const rect = el.getBoundingRect();
+        const center = [
+            rect.x + rect.width,
+            rect.y + rect.height
+        ];
+
+        const mat = matrix.identity(TMP_TRANSFORM);
+
+        let target = el;
+        while (target && !(target as GeoSVGGraphicRoot).isGeoSVGGraphicRoot) {
+            matrix.mul(mat, target.getLocalTransform(), mat);
+            target = target.parent;
+        }
+
+        matrix.invert(mat, mat);
+
+        vec2.applyTransform(center, center, mat);
+
+        return center;
+    }
+
 }
 
-export default Region;
diff --git a/src/coord/geo/fix/diaoyuIsland.ts b/src/coord/geo/fix/diaoyuIsland.ts
index 06042cc..b845900 100644
--- a/src/coord/geo/fix/diaoyuIsland.ts
+++ b/src/coord/geo/fix/diaoyuIsland.ts
@@ -1,5 +1,3 @@
-import Region from '../Region';
-
 /*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
@@ -19,6 +17,8 @@ import Region from '../Region';
 * under the License.
 */
 
+import { GeoJSONRegion } from '../Region';
+
 // Fix for 钓鱼岛
 
 // let Region = require('../Region');
@@ -36,7 +36,7 @@ const points = [
     ]
 ];
 
-export default function fixDiaoyuIsland(mapType: string, region: Region) {
+export default function fixDiaoyuIsland(mapType: string, region: GeoJSONRegion) {
     if (mapType === 'china' && region.name === '台湾') {
         region.geometries.push({
             type: 'polygon',
diff --git a/src/coord/geo/fix/geoCoord.ts b/src/coord/geo/fix/geoCoord.ts
index bfef6e9..ea03c64 100644
--- a/src/coord/geo/fix/geoCoord.ts
+++ b/src/coord/geo/fix/geoCoord.ts
@@ -1,6 +1,3 @@
-import Region from '../Region';
-import { Dictionary } from 'zrender/src/core/types';
-
 /*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
@@ -20,19 +17,24 @@ import { Dictionary } from 'zrender/src/core/types';
 * under the License.
 */
 
+import { Dictionary } from 'zrender/src/core/types';
+import { GeoJSONRegion } from '../Region';
+
 const geoCoordMap = {
     'Russia': [100, 60],
     'United States': [-99, 38],
     'United States of America': [-99, 38]
 } as Dictionary<number[]>;
 
-export default function fixGeoCoords(mapType: string, region: Region) {
+export default function fixGeoCoords(mapType: string, region: GeoJSONRegion) {
     if (mapType === 'world') {
         const geoCoord = geoCoordMap[region.name];
         if (geoCoord) {
-            const cp = region.center;
-            cp[0] = geoCoord[0];
-            cp[1] = geoCoord[1];
+            const cp = [
+                geoCoord[0],
+                geoCoord[1]
+            ];
+            region.setCenter(cp);
         }
     }
 }
\ No newline at end of file
diff --git a/src/coord/geo/fix/nanhai.ts b/src/coord/geo/fix/nanhai.ts
index 2bbc3d0..e1dd612 100644
--- a/src/coord/geo/fix/nanhai.ts
+++ b/src/coord/geo/fix/nanhai.ts
@@ -20,7 +20,7 @@
 // Fix for 南海诸岛
 
 import * as zrUtil from 'zrender/src/core/util';
-import Region from '../Region';
+import { GeoJSONRegion } from '../Region';
 
 const geoCoord = [126, 25];
 
@@ -51,9 +51,9 @@ for (let i = 0; i < points.length; i++) {
     }
 }
 
-export default function fixNanhai(mapType: string, regions: Region[]) {
+export default function fixNanhai(mapType: string, regions: GeoJSONRegion[]) {
     if (mapType === 'china') {
-        regions.push(new Region(
+        regions.push(new GeoJSONRegion(
             '南海诸岛',
             zrUtil.map(points, function (exterior) {
                 return {
diff --git a/src/coord/geo/fix/textCoord.ts b/src/coord/geo/fix/textCoord.ts
index 477d644..813e26a 100644
--- a/src/coord/geo/fix/textCoord.ts
+++ b/src/coord/geo/fix/textCoord.ts
@@ -1,6 +1,3 @@
-import Region from '../Region';
-import { Dictionary } from 'zrender/src/core/types';
-
 /*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
@@ -20,6 +17,8 @@ import { Dictionary } from 'zrender/src/core/types';
 * under the License.
 */
 
+import { GeoJSONRegion } from '../Region';
+import { Dictionary } from 'zrender/src/core/types';
 
 const coordsOffsetMap = {
     '南海诸岛': [32, 80],
@@ -31,13 +30,14 @@ const coordsOffsetMap = {
     '天津': [5, 5]
 } as Dictionary<number[]>;
 
-export default function fixTextCoords(mapType: string, region: Region) {
+export default function fixTextCoords(mapType: string, region: GeoJSONRegion) {
     if (mapType === 'china') {
         const coordFix = coordsOffsetMap[region.name];
         if (coordFix) {
-            const cp = region.center;
+            const cp = region.getCenter();
             cp[0] += coordFix[0] / 10.5;
             cp[1] += -coordFix[1] / (10.5 / 0.75);
+            region.setCenter(cp);
         }
     }
 }
\ No newline at end of file
diff --git a/src/coord/geo/geoCreator.ts b/src/coord/geo/geoCreator.ts
index e01f0d8..49ae8d5 100644
--- a/src/coord/geo/geoCreator.ts
+++ b/src/coord/geo/geoCreator.ts
@@ -35,6 +35,7 @@ import ComponentModel from '../../model/Component';
 
 
 export type resizeGeoType = typeof resizeGeo;
+
 /**
  * Resize method bound to the geo
  */
@@ -138,21 +139,11 @@ class GeoCreator implements CoordinateSystemCreator {
         ecModel.eachComponent('geo', function (geoModel: GeoModel, idx) {
             const name = geoModel.get('map');
 
-            let aspectScale = geoModel.get('aspectScale');
-            let invertLongitute = true;
-
-            const geoResource = geoSourceManager.getGeoResource(name);
-            if (geoResource.type === 'svg') {
-                aspectScale == null && (aspectScale = 1);
-                invertLongitute = false;
-            }
-            else {
-                aspectScale == null && (aspectScale = 0.75);
-            }
-
-            const geo = new Geo(name + idx, name, geoModel.get('nameMap'), invertLongitute);
+            const geo = new Geo(name + idx, name, {
+                nameMap: geoModel.get('nameMap'),
+                aspectScale: geoModel.get('aspectScale')
+            });
 
-            geo.aspectScale = aspectScale;
             geo.zoomLimit = geoModel.get('scaleLimit');
             geoList.push(geo);
 
@@ -192,7 +183,11 @@ class GeoCreator implements CoordinateSystemCreator {
             const nameMapList = zrUtil.map(mapSeries, function (singleMapSeries) {
                 return singleMapSeries.get('nameMap');
             });
-            const geo = new Geo(mapType, mapType, zrUtil.mergeAll(nameMapList));
+
+            const geo = new Geo(mapType, mapType, {
+                nameMap: zrUtil.mergeAll(nameMapList),
+                aspectScale: mapSeries[0].get('aspectScale')
+            });
 
             geo.zoomLimit = zrUtil.retrieve.apply(null, zrUtil.map(mapSeries, function (singleMapSeries) {
                 return singleMapSeries.get('scaleLimit');
@@ -201,7 +196,6 @@ class GeoCreator implements CoordinateSystemCreator {
 
             // Inject resize method
             geo.resize = resizeGeo;
-            geo.aspectScale = mapSeries[0].get('aspectScale');
 
             geo.resize(mapSeries[0], api);
 
@@ -239,6 +233,7 @@ class GeoCreator implements CoordinateSystemCreator {
     }
 }
 
+
 const geoCreator = new GeoCreator();
 
 export default geoCreator;
diff --git a/src/coord/geo/geoSourceManager.ts b/src/coord/geo/geoSourceManager.ts
index 66cdf1e..f13d9ec 100644
--- a/src/coord/geo/geoSourceManager.ts
+++ b/src/coord/geo/geoSourceManager.ts
@@ -131,7 +131,7 @@ export default {
             && (resource as GeoJSONResource).getMapForUser();
     },
 
-    load: function (mapName: string, nameMap: NameMap, nameProperty?: string) {
+    load: function (mapName: string, nameMap: NameMap, nameProperty?: string): ReturnType<GeoResource['load']> {
         const resource = storage.get(mapName);
 
         if (!resource) {
diff --git a/src/coord/geo/geoTypes.ts b/src/coord/geo/geoTypes.ts
index 52b8eb2..303db25 100644
--- a/src/coord/geo/geoTypes.ts
+++ b/src/coord/geo/geoTypes.ts
@@ -19,7 +19,8 @@
 
 import BoundingRect from 'zrender/src/core/BoundingRect';
 import { HashMap } from 'zrender/src/core/util';
-import Region from './Region';
+import { Group } from '../../util/graphic';
+import { Region } from './Region';
 
 
 export type GeoSVGSourceInput = 'string' | Document | SVGElement;
@@ -123,13 +124,17 @@ interface GeoJSONGeometryMultiPolygonCompressed {
 // };
 
 export interface GeoResource {
-    readonly type: 'geoJSON' | 'svg';
+    readonly type: 'geoJSON' | 'geoSVG';
     load(nameMap: NameMap, nameProperty: string): {
         boundingRect: BoundingRect;
-        regions?: Region[];
-        // Key: mapName
-        regionsMap?: HashMap<Region>;
-        // Key: mapName
-        nameCoordMap?: HashMap<number[]>;
+        regions: Region[];
+        // Key: region.name
+        regionsMap: HashMap<Region>;
+        // Key: region.name
+        nameCoordMap: HashMap<number[]>;
     };
 }
+
+export interface GeoSVGGraphicRoot extends Group {
+    isGeoSVGGraphicRoot: boolean;
+}
diff --git a/src/coord/geo/parseGeoJson.ts b/src/coord/geo/parseGeoJson.ts
index e37260e..7b2b920 100644
--- a/src/coord/geo/parseGeoJson.ts
+++ b/src/coord/geo/parseGeoJson.ts
@@ -22,7 +22,7 @@
  */
 
 import * as zrUtil from 'zrender/src/core/util';
-import Region from './Region';
+import { GeoJSONRegion } from './Region';
 import { GeoJSONCompressed, GeoJSON } from './geoTypes';
 
 
@@ -100,7 +100,7 @@ function decodePolygon(
     return result;
 }
 
-export default function parseGeoJSON(geoJson: GeoJSON | GeoJSONCompressed, nameProperty: string): Region[] {
+export default function parseGeoJSON(geoJson: GeoJSON | GeoJSONCompressed, nameProperty: string): GeoJSONRegion[] {
 
     geoJson = decode(geoJson);
 
@@ -113,7 +113,7 @@ export default function parseGeoJSON(geoJson: GeoJSON | GeoJSONCompressed, nameP
         const properties = featureObj.properties;
         const geo = featureObj.geometry;
 
-        const geometries = [] as Region['geometries'];
+        const geometries = [] as GeoJSONRegion['geometries'];
         if (geo.type === 'Polygon') {
             const coordinates = geo.coordinates;
             geometries.push({
@@ -137,7 +137,7 @@ export default function parseGeoJSON(geoJson: GeoJSON | GeoJSONCompressed, nameP
             });
         }
 
-        const region = new Region(
+        const region = new GeoJSONRegion(
             properties[nameProperty || 'name'],
             geometries,
             properties.cp
diff --git a/test/geo-svg.html b/test/geo-svg.html
index d7568d5..70b6139 100644
--- a/test/geo-svg.html
+++ b/test/geo-svg.html
@@ -39,6 +39,7 @@ under the License.
 
         <div id="main0"></div>
         <div id="main1"></div>
+        <div id="main2"></div>
 
 
 
@@ -72,6 +73,7 @@ under the License.
                 geo: {
                     map: 'testGeoJson1',
                     roam: true,
+                    selectedMode: 'single',
                     // height: '100%',
                     // center
                     // layoutCenter: ['30%', 40],
@@ -89,9 +91,22 @@ under the License.
                     'At the center of the canvas.'
                 ],
                 option: option,
+                info: [],
+                infoKey: 'allSelected',
                 height: 300
             });
 
+            if (chart) {
+                if (chart) {
+                    chart.on('geoselectchanged', function (params) {
+                        chart.__testHelper.updateInfo(
+                            params.allSelected,
+                            'allSelected'
+                        );
+                    });
+                }
+            }
+
 
         });
         </script>
@@ -102,11 +117,7 @@ under the License.
         require(['echarts'/*, 'map/js/china' */], function (echarts) {
             var option;
             $.ajax({
-                url: '../../vis-data/map/svg/seats/seatmap-example.svg', // 剧场例子
-                // url: '../../vis-data/map/svg/seats/Ethiopian_Airlines_Flight_961_seating_plan.svg', // 飞机例子
-                // url: '../../vis-data/map/svg/seats/oracle-seating-map-2017-2.svg', // 渲染错误
-                // url: '../../vis-data/map/svg/seats/DC-10-30_seat_configuration_chart.svg', // 渲染错误
-                // url: '../../vis-data/map/svg/seats/Airbus_A300B4-622R_seat_configuration_chart.svg', // 渲染错误
+                url: '../../vis-data/map/svg/seats/seatmap-example.svg',
                 dataType: 'text'
             }).done(function (svg) {
 
@@ -118,24 +129,25 @@ under the License.
                     geo: {
                         map: 'seatmap',
                         roam: true,
+                        selectedMode: 'multiple',
                         // height: 100,
                         // zoom: 1.5
                         emphasis: {
-                            // itemStyle: {
-                            //     color: 'red'
-                            // },
                             label: {
-                                // color: '#fff',
                                 textBorderColor: '#fff',
                                 textBorderWidth: 2
                             }
                         },
-                        // itemStyle: {
-                        //     color: 'red'
-                        // },
-                        // label: {
-                        //     color: '#fff'
-                        // }
+                        select: {
+                            itemStyle: {
+                                color: '#b50205'
+                            },
+                            label: {
+                                show: false,
+                                textBorderColor: '#fff',
+                                textBorderWidth: 2
+                            }
+                        }
                     },
                     // series: {
                     //     type: 'scatter',
@@ -148,15 +160,26 @@ under the License.
 
                 var chart = testHelper.create(echarts, 'main1', {
                     title: [
-                        'Test Case Description of main0',
-                        '(Muliple lines and **emphasis** are supported in description)'
+                        'geo component with svg resource',
+                        'click seat: check **allSelected** correct.'
                     ],
                     option: option,
+                    info: [],
+                    infoKey: 'allSelected',
                     height: 300
                     // buttons: [{text: 'btn-txt', onclick: function () {}}],
                     // recordCanvas: true,
                 });
 
+                if (chart) {
+                    chart.on('geoselectchanged', function (params) {
+                        chart.__testHelper.updateInfo(
+                            params.allSelected,
+                            'allSelected'
+                        );
+                    });
+                }
+
             });
 
         });
@@ -165,6 +188,77 @@ under the License.
 
 
 
+
+
+        <script>
+        require(['echarts'/*, 'map/js/china' */], function (echarts) {
+            var option;
+            $.ajax({
+                url: '../../vis-data/map/svg/seats/seatmap-example.svg',
+                dataType: 'text'
+            }).done(function (svg) {
+
+                echarts.registerMap('seatmap', {
+                    svg: svg
+                });
+
+                option = {
+                    tooltip: {
+                    },
+                    series: {
+                        type: 'map',
+                        map: 'seatmap',
+                        roam: true,
+                        selectedMode: 'multiple',
+                        // height: 100,
+                        // zoom: 1.5
+                        emphasis: {
+                            label: {
+                                textBorderColor: '#fff',
+                                textBorderWidth: 2
+                            }
+                        },
+                        select: {
+                            itemStyle: {
+                                color: '#b50205'
+                            },
+                            label: {
+                                show: false,
+                                textBorderColor: '#fff',
+                                textBorderWidth: 2
+                            }
+                        }
+                    }
+                };
+
+                var chart = testHelper.create(echarts, 'main2', {
+                    title: [
+                        'map series with svg resource',
+                        'Hover seat: check **tooltip** correct.'
+                    ],
+                    option: option,
+                    info: [],
+                    infoKey: 'allSelected',
+                    height: 300
+                    // buttons: [{text: 'btn-txt', onclick: function () {}}],
+                    // recordCanvas: true,
+                });
+
+                if (chart) {
+                    chart.on('selectchanged', function (params) {
+                        chart.__testHelper.updateInfo(
+                            params.selected,
+                            'selected'
+                        );
+                    });
+                }
+
+            });
+
+        });
+        </script>
+
+
     </body>
 </html>
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org


[echarts] 05/06: feature: [geo] support geo svg named elements have the same behavior as regions of geoJSON

Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a commit to branch fix/geo-svg
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit ca5817445241ab7ccc55853ac8049b662b632449
Author: 100pah <su...@gmail.com>
AuthorDate: Tue Mar 9 12:51:20 2021 +0800

    feature: [geo] support geo svg named elements have the same behavior as regions of geoJSON
---
 src/component/helper/MapDraw.ts | 386 +++++++++++++++++++++++-----------------
 src/coord/geo/GeoModel.ts       |   1 -
 src/coord/geo/GeoSVGResource.ts |   6 +-
 test/geo-svg.html               | 170 ++++++++++++++++++
 4 files changed, 398 insertions(+), 165 deletions(-)

diff --git a/src/component/helper/MapDraw.ts b/src/component/helper/MapDraw.ts
index 1ca9ad9..a7176bf 100644
--- a/src/component/helper/MapDraw.ts
+++ b/src/component/helper/MapDraw.ts
@@ -39,11 +39,24 @@ import { getECData } from '../../util/innerStore';
 import { createOrUpdatePatternFromDecal } from '../../util/decal';
 import { ViewCoordSysTransformInfoPart } from '../../coord/View';
 import { GeoSVGResource } from '../../coord/geo/GeoSVGResource';
+import Displayable from 'zrender/src/graphic/Displayable';
+import Element, { ElementTextConfig } from 'zrender/src/Element';
+import List from '../../data/List';
 
 
 interface RegionsGroup extends graphic.Group {
 }
 
+interface ViewBuildContext {
+    api: ExtensionAPI;
+    geo: Geo;
+    mapOrGeoModel: GeoModel | MapSeries;
+    data: List;
+    isVisualEncodedByVisualMap: boolean;
+    isGeo: boolean;
+    transformInfoRaw: ViewCoordSysTransformInfoPart;
+}
+
 function getFixedItemStyle(model: Model<GeoItemStyleOption>) {
     const itemStyle = model.getItemStyle();
     const areaColor = model.get('areaColor');
@@ -78,12 +91,14 @@ class MapDraw {
      */
     private _mouseDownFlag: boolean;
 
-    private _mapName: string;
+    private _svgMapName: string;
 
     private _regionsGroup: RegionsGroup;
 
     private _svgGroup: graphic.Group;
 
+    private _svgNamedElements: Displayable[];
+
 
     constructor(api: ExtensionAPI) {
         const group = new graphic.Group();
@@ -117,7 +132,6 @@ class MapDraw {
 
         const geo = mapOrGeoModel.coordinateSystem;
 
-
         const regionsGroup = this._regionsGroup;
         const group = this.group;
 
@@ -125,8 +139,6 @@ class MapDraw {
         const transformInfoRaw = transformInfo.raw;
         const transformInfoRoam = transformInfo.roam;
 
-        this._updateSVG(geo, transformInfoRaw);
-
         // No animation when first draw or in action
         const isFirstDraw = !regionsGroup.childAt(0) || payload;
 
@@ -141,17 +153,44 @@ class MapDraw {
             graphic.updateProps(group, transformInfoRoam, mapOrGeoModel);
         }
 
-        regionsGroup.removeAll();
-
-        const nameMap = zrUtil.createHashMap<RegionsGroup>();
-
-
         const isVisualEncodedByVisualMap = data
             && data.getVisual('visualMeta')
             && data.getVisual('visualMeta').length > 0;
 
+        const viewBuildCtx = {
+            api,
+            geo,
+            mapOrGeoModel,
+            data,
+            isVisualEncodedByVisualMap,
+            isGeo,
+            transformInfoRaw
+        };
+
+        this._buildGeoJSON(viewBuildCtx);
+        this._buildSVG(viewBuildCtx);
+
+        this._updateController(mapOrGeoModel, ecModel, api);
+
+        this._updateMapSelectHandler(mapOrGeoModel, regionsGroup, api, fromView);
+    }
+
+    private _buildGeoJSON(viewBuildCtx: ViewBuildContext): void {
+        const nameMap = zrUtil.createHashMap<RegionsGroup>();
+        const regionsGroup = this._regionsGroup;
+        const transformInfoRaw = viewBuildCtx.transformInfoRaw;
+
+        const transformPoint = function (point: number[]): number[] {
+            return [
+                point[0] * transformInfoRaw.scaleX + transformInfoRaw.x,
+                point[1] * transformInfoRaw.scaleY + transformInfoRaw.y
+            ];
+        };
+
+        regionsGroup.removeAll();
 
-        zrUtil.each(geo.regions, function (region) {
+        // Only when the resource is GeoJSON, there is `geo.regions`.
+        zrUtil.each(viewBuildCtx.geo.regions, function (region) {
 
             // Consider in GeoJson properties.name may be duplicated, for example,
             // there is multiple region named "United Kindom" or "France" (so many
@@ -169,50 +208,6 @@ class MapDraw {
             });
             regionGroup.add(compoundPath);
 
-            const regionModel = mapOrGeoModel.getRegionModel(region.name) || mapOrGeoModel;
-
-            // @ts-ignore FIXME:TS fix the "compatible with each other"?
-            const itemStyleModel = regionModel.getModel('itemStyle');
-            // @ts-ignore FIXME:TS fix the "compatible with each other"?
-            const emphasisModel = regionModel.getModel('emphasis');
-            const emphasisItemStyleModel = emphasisModel.getModel('itemStyle');
-            // @ts-ignore FIXME:TS fix the "compatible with each other"?
-            const blurItemStyleModel = regionModel.getModel(['blur', 'itemStyle']);
-            // @ts-ignore FIXME:TS fix the "compatible with each other"?
-            const selectItemStyleModel = regionModel.getModel(['select', 'itemStyle']);
-
-            // NOTE: DONT use 'style' in visual when drawing map.
-            // This component is used for drawing underlying map for both geo component and map series.
-            const itemStyle = getFixedItemStyle(itemStyleModel);
-            const emphasisItemStyle = getFixedItemStyle(emphasisItemStyleModel);
-            const blurItemStyle = getFixedItemStyle(blurItemStyleModel);
-            const selectItemStyle = getFixedItemStyle(selectItemStyleModel);
-
-            let dataIdx;
-            // Use the itemStyle in data if has data
-            if (data) {
-                dataIdx = data.indexOfName(region.name);
-                // Only visual color of each item will be used. It can be encoded by visualMap
-                // But visual color of series is used in symbol drawing
-                //
-                // Visual color for each series is for the symbol draw
-                const style = data.getItemVisual(dataIdx, 'style');
-                const decal = data.getItemVisual(dataIdx, 'decal');
-                if (isVisualEncodedByVisualMap && style.fill) {
-                    itemStyle.fill = style.fill;
-                }
-                if (decal) {
-                    itemStyle.decal = createOrUpdatePatternFromDecal(decal, api);
-                }
-            }
-
-            const transformPoint = function (point: number[]): number[] {
-                return [
-                    point[0] * transformInfoRaw.scaleX + transformInfoRaw.x,
-                    point[1] * transformInfoRaw.scaleY + transformInfoRaw.y
-                ];
-            };
-
             zrUtil.each(region.geometries, function (geometry) {
                 if (geometry.type !== 'polygon') {
                     return;
@@ -243,149 +238,218 @@ class MapDraw {
                 }
             });
 
-            compoundPath.setStyle(itemStyle);
-            compoundPath.style.strokeNoScale = true;
-            compoundPath.culling = true;
-
-            compoundPath.ensureState('emphasis').style = emphasisItemStyle;
-            compoundPath.ensureState('blur').style = blurItemStyle;
-            compoundPath.ensureState('select').style = selectItemStyle;
-
-            let showLabel = false;
-            for (let i = 0; i < DISPLAY_STATES.length; i++) {
-                const stateName = DISPLAY_STATES[i];
-                // @ts-ignore FIXME:TS fix the "compatible with each other"?
-                if (regionModel.get(
-                    stateName === 'normal' ? ['label', 'show'] : [stateName, 'label', 'show']
-                )) {
-                    showLabel = true;
-                    break;
-                }
-            }
+            const centerPt = transformPoint(region.center);
 
-            const isDataNaN = data && isNaN(data.get(data.mapDimension('value'), dataIdx) as number);
-            const itemLayout = data && data.getItemLayout(dataIdx);
-
-            // In the following cases label will be drawn
-            // 1. In map series and data value is NaN
-            // 2. In geo component
-            // 4. Region has no series legendSymbol, which will be add a showLabel flag in mapSymbolLayout
-            if (
-                (isGeo || isDataNaN && (showLabel))
-                || (itemLayout && itemLayout.showLabel)
-            ) {
-                const query = !isGeo ? dataIdx : region.name;
-                let labelFetcher;
-
-                // Consider dataIdx not found.
-                if (!data || dataIdx >= 0) {
-                    labelFetcher = mapOrGeoModel;
-                }
+            this._resetSingleRegionGraphic(
+                viewBuildCtx, compoundPath, regionGroup, region.name, centerPt, null
+            );
 
-                const centerPt = transformPoint(region.center);
-                const textEl = new graphic.Text({
-                    x: centerPt[0],
-                    y: centerPt[1],
-                    z2: 10,
-                    silent: true
-                });
-                textEl.afterUpdate = labelTextAfterUpdate;
-
-                setLabelStyle<typeof query>(
-                    textEl, getLabelStatesModels(regionModel),
-                    {
-                        labelFetcher: labelFetcher,
-                        labelDataIndex: query,
-                        defaultText: region.name
-                    },
-                    { normal: {
-                        align: 'center',
-                        verticalAlign: 'middle'
-                    } }
-                );
-
-                compoundPath.setTextContent(textEl);
-                compoundPath.setTextConfig({
-                    local: true
-                });
-
-                (compoundPath as ECElement).disableLabelAnimation = true;
+            regionsGroup.add(regionGroup);
 
-            }
+        }, this);
+    }
+
+    private _buildSVG(viewBuildCtx: ViewBuildContext): void {
+        const mapName = viewBuildCtx.geo.map;
+        const transformInfoRaw = viewBuildCtx.transformInfoRaw;
+
+        this._svgGroup.x = transformInfoRaw.x;
+        this._svgGroup.y = transformInfoRaw.y;
+        this._svgGroup.scaleX = transformInfoRaw.scaleX;
+        this._svgGroup.scaleY = transformInfoRaw.scaleY;
+
+        if (this._svgResourceChanged(mapName)) {
+            this._freeSVG();
+            this._useSVG(mapName);
+        }
 
-            // setItemGraphicEl, setHoverStyle after all polygons and labels
-            // are added to the rigionGroup
-            if (data) {
-                data.setItemGraphicEl(dataIdx, regionGroup);
+        zrUtil.each(this._svgNamedElements, function (namedElement) {
+            this._resetSingleRegionGraphic(
+                viewBuildCtx, namedElement, namedElement, namedElement.name, [0, 0], 'inside'
+            );
+        }, this);
+    }
+
+    private _resetSingleRegionGraphic(
+        viewBuildCtx: ViewBuildContext,
+        displayable: Displayable,
+        elForStateChange: Element,
+        regionName: string,
+        labelXY: number[],
+        labelPosition: ElementTextConfig['position']
+    ): void {
+
+        const mapOrGeoModel = viewBuildCtx.mapOrGeoModel;
+        const data = viewBuildCtx.data;
+        const isVisualEncodedByVisualMap = viewBuildCtx.isVisualEncodedByVisualMap;
+        const isGeo = viewBuildCtx.isGeo;
+
+        const regionModel = mapOrGeoModel.getRegionModel(regionName) || mapOrGeoModel;
+
+        // @ts-ignore FIXME:TS fix the "compatible with each other"?
+        const itemStyleModel = regionModel.getModel('itemStyle');
+        // @ts-ignore FIXME:TS fix the "compatible with each other"?
+        const emphasisModel = regionModel.getModel('emphasis');
+        const emphasisItemStyleModel = emphasisModel.getModel('itemStyle');
+        // @ts-ignore FIXME:TS fix the "compatible with each other"?
+        const blurItemStyleModel = regionModel.getModel(['blur', 'itemStyle']);
+        // @ts-ignore FIXME:TS fix the "compatible with each other"?
+        const selectItemStyleModel = regionModel.getModel(['select', 'itemStyle']);
+
+        // NOTE: DONT use 'style' in visual when drawing map.
+        // This component is used for drawing underlying map for both geo component and map series.
+        const itemStyle = getFixedItemStyle(itemStyleModel);
+        const emphasisItemStyle = getFixedItemStyle(emphasisItemStyleModel);
+        const blurItemStyle = getFixedItemStyle(blurItemStyleModel);
+        const selectItemStyle = getFixedItemStyle(selectItemStyleModel);
+
+        let dataIdx;
+        // Use the itemStyle in data if has data
+        if (data) {
+            dataIdx = data.indexOfName(regionName);
+            // Only visual color of each item will be used. It can be encoded by visualMap
+            // But visual color of series is used in symbol drawing
+            //
+            // Visual color for each series is for the symbol draw
+            const style = data.getItemVisual(dataIdx, 'style');
+            const decal = data.getItemVisual(dataIdx, 'decal');
+            if (isVisualEncodedByVisualMap && style.fill) {
+                itemStyle.fill = style.fill;
             }
-            else {
-                const regionModel = mapOrGeoModel.getRegionModel(region.name);
-                // Package custom mouse event for geo component
-                getECData(compoundPath).eventData = {
-                    componentType: 'geo',
-                    componentIndex: mapOrGeoModel.componentIndex,
-                    geoIndex: mapOrGeoModel.componentIndex,
-                    name: region.name,
-                    region: (regionModel && regionModel.option) || {}
-                };
+            if (decal) {
+                itemStyle.decal = createOrUpdatePatternFromDecal(decal, viewBuildCtx.api);
             }
+        }
 
+        displayable.setStyle(itemStyle);
+        displayable.style.strokeNoScale = true;
+        displayable.culling = true;
+
+        displayable.ensureState('emphasis').style = emphasisItemStyle;
+        displayable.ensureState('blur').style = blurItemStyle;
+        displayable.ensureState('select').style = selectItemStyle;
+
+
+        let showLabel = false;
+        for (let i = 0; i < DISPLAY_STATES.length; i++) {
+            const stateName = DISPLAY_STATES[i];
             // @ts-ignore FIXME:TS fix the "compatible with each other"?
-            regionGroup.highDownSilentOnTouch = !!mapOrGeoModel.get('selectedMode');
-            enableHoverEmphasis(regionGroup, emphasisModel.get('focus'), emphasisModel.get('blurScope'));
+            if (regionModel.get(
+                stateName === 'normal' ? ['label', 'show'] : [stateName, 'label', 'show']
+            )) {
+                showLabel = true;
+                break;
+            }
+        }
 
-            regionsGroup.add(regionGroup);
-        });
+        const isDataNaN = data && isNaN(data.get(data.mapDimension('value'), dataIdx) as number);
+        const itemLayout = data && data.getItemLayout(dataIdx);
+
+        // In the following cases label will be drawn
+        // 1. In map series and data value is NaN
+        // 2. In geo component
+        // 4. Region has no series legendSymbol, which will be add a showLabel flag in mapSymbolLayout
+        if (
+            (isGeo || isDataNaN && (showLabel))
+            || (itemLayout && itemLayout.showLabel)
+        ) {
+            const query = !isGeo ? dataIdx : regionName;
+            let labelFetcher;
+
+            // Consider dataIdx not found.
+            if (!data || dataIdx >= 0) {
+                labelFetcher = mapOrGeoModel;
+            }
 
-        this._updateController(mapOrGeoModel, ecModel, api);
+            const textEl = new graphic.Text({
+                x: labelXY[0],
+                y: labelXY[1],
+                z2: 10,
+                silent: true
+            });
+            textEl.afterUpdate = labelTextAfterUpdate;
+
+            setLabelStyle<typeof query>(
+                textEl, getLabelStatesModels(regionModel),
+                {
+                    labelFetcher: labelFetcher,
+                    labelDataIndex: query,
+                    defaultText: regionName
+                },
+                { normal: {
+                    align: 'center',
+                    verticalAlign: 'middle'
+                } }
+            );
+
+            displayable.setTextContent(textEl);
+            displayable.setTextConfig({
+                local: true,
+                insideFill: textEl.style.fill,
+                position: labelPosition
+            });
+
+            (displayable as ECElement).disableLabelAnimation = true;
+        }
+
+
+        // setItemGraphicEl, setHoverStyle after all polygons and labels
+        // are added to the rigionGroup
+        if (data) {
+            data.setItemGraphicEl(dataIdx, elForStateChange);
+        }
+        else {
+            const regionModel = mapOrGeoModel.getRegionModel(regionName);
+            // Package custom mouse event for geo component
+            getECData(displayable).eventData = {
+                componentType: 'geo',
+                componentIndex: mapOrGeoModel.componentIndex,
+                geoIndex: mapOrGeoModel.componentIndex,
+                name: regionName,
+                region: (regionModel && regionModel.option) || {}
+            };
+        }
+
+        // @ts-ignore FIXME:TS fix the "compatible with each other"?
+        elForStateChange.highDownSilentOnTouch = !!mapOrGeoModel.get('selectedMode');
+        enableHoverEmphasis(elForStateChange, emphasisModel.get('focus'), emphasisModel.get('blurScope'));
 
-        this._updateMapSelectHandler(mapOrGeoModel, regionsGroup, api, fromView);
     }
 
     remove(): void {
         this._regionsGroup.removeAll();
         this._svgGroup.removeAll();
         this._controller.dispose();
-        this._freeSVG(this._mapName);
-        this._mapName = null;
+        this._freeSVG();
         this._controllerHost = null;
     }
 
-    private _updateSVG(geo: Geo, transformInfoRaw: ViewCoordSysTransformInfoPart): void {
-        const mapName = geo.map;
-
-        this._svgGroup.x = transformInfoRaw.x;
-        this._svgGroup.y = transformInfoRaw.y;
-        this._svgGroup.scaleX = transformInfoRaw.scaleX;
-        this._svgGroup.scaleY = transformInfoRaw.scaleY;
-
-        if (this._mapName !== mapName) {
-            this._freeSVG(this._mapName);
-            this._useSVG(mapName);
-            this._mapName = mapName;
-        }
+    private _svgResourceChanged(mapName: string): boolean {
+        return this._svgMapName !== mapName;
     }
 
-    private _useSVG(mapName: string) {
-        if (mapName == null) {
-            return;
-        }
+    private _useSVG(mapName: string): void {
         const resource = geoSourceManager.getGeoResource(mapName);
         if (resource && resource.type === 'svg') {
             const svgGraphic = (resource as GeoSVGResource).useGraphic(this.uid);
             this._svgGroup.add(svgGraphic.root);
+            this._svgNamedElements = svgGraphic.namedElements;
+            this._svgMapName = mapName;
         }
     }
 
-    private _freeSVG(mapName: string) {
+    private _freeSVG(): void {
+        const mapName = this._svgMapName;
         if (mapName == null) {
             return;
         }
         const resource = geoSourceManager.getGeoResource(mapName);
         if (resource && resource.type === 'svg') {
             (resource as GeoSVGResource).freeGraphic(this.uid);
-            this._svgGroup.removeAll();
         }
+        this._svgGroup.removeAll();
+        this._svgNamedElements = null;
+        this._svgMapName = null;
     }
 
     private _updateController(
diff --git a/src/coord/geo/GeoModel.ts b/src/coord/geo/GeoModel.ts
index 3705304..78054c6 100644
--- a/src/coord/geo/GeoModel.ts
+++ b/src/coord/geo/GeoModel.ts
@@ -163,7 +163,6 @@ class GeoModel extends ComponentModel<GeoOption> {
         },
 
         itemStyle: {
-            // color: 各异,
             borderWidth: 0.5,
             borderColor: '#444',
             color: '#eee'
diff --git a/src/coord/geo/GeoSVGResource.ts b/src/coord/geo/GeoSVGResource.ts
index 0a3a4ec..43c18fa 100644
--- a/src/coord/geo/GeoSVGResource.ts
+++ b/src/coord/geo/GeoSVGResource.ts
@@ -24,11 +24,11 @@ import {assert, createHashMap, HashMap} from 'zrender/src/core/util';
 import BoundingRect from 'zrender/src/core/BoundingRect';
 import { GeoResource, GeoSVGSourceInput } from './geoTypes';
 import { parseXML } from 'zrender/src/tool/parseXML';
-import Element from 'zrender/src/Element';
+import Displayable from 'zrender/src/graphic/Displayable';
 
 export interface GeoSVGGraphic {
     root: Group;
-    namedElements: Element[];
+    namedElements: Displayable[];
 }
 
 export class GeoSVGResource implements GeoResource {
@@ -115,7 +115,7 @@ function buildGraphic(
 ): {
     root: Group;
     boundingRect: BoundingRect;
-    namedElements: Element[]
+    namedElements: Displayable[]
 } {
     let result;
     let root;
diff --git a/test/geo-svg.html b/test/geo-svg.html
new file mode 100644
index 0000000..d7568d5
--- /dev/null
+++ b/test/geo-svg.html
@@ -0,0 +1,170 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+
+
+<html>
+    <head>
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1" />
+        <script src="lib/esl.js"></script>
+        <script src="lib/config.js"></script>
+        <script src="lib/jquery.min.js"></script>
+        <script src="lib/facePrint.js"></script>
+        <script src="lib/testHelper.js"></script>
+        <!-- <script src="ut/lib/canteen.js"></script> -->
+        <link rel="stylesheet" href="lib/reset.css" />
+    </head>
+    <body>
+        <style>
+        </style>
+
+
+
+        <div id="main0"></div>
+        <div id="main1"></div>
+
+
+
+
+
+
+        <script>
+        require(['echarts'/*, 'map/js/china' */], function (echarts) {
+            const testGeoJson1 = {
+                'type': 'FeatureCollection',
+                'features': [
+                    {
+                        'type': 'Feature',
+                        'geometry': {
+                            'type': 'Polygon',
+                            'coordinates': [
+                                [[2000, 2000], [5000, 2000], [5000, 5000], [2000, 5000]]
+                            ]
+                        },
+                        'properties': {
+                            'name': 'Afghanistan',
+                            'childNum': 1
+                        }
+                    }
+                ]
+            };
+
+            echarts.registerMap('testGeoJson1', testGeoJson1);
+
+            option = {
+                geo: {
+                    map: 'testGeoJson1',
+                    roam: true,
+                    // height: '100%',
+                    // center
+                    // layoutCenter: ['30%', 40],
+                    // layoutSize: 40,
+                    // boundingCoords
+                    zoom: 1,
+                    aspectScale: 1
+                }
+            };
+
+            var chart = testHelper.create(echarts, 'main0', {
+                title: [
+                    'geoJSON location:',
+                    'Should be a square and 80% of canvas height.',
+                    'At the center of the canvas.'
+                ],
+                option: option,
+                height: 300
+            });
+
+
+        });
+        </script>
+
+
+
+        <script>
+        require(['echarts'/*, 'map/js/china' */], function (echarts) {
+            var option;
+            $.ajax({
+                url: '../../vis-data/map/svg/seats/seatmap-example.svg', // 剧场例子
+                // url: '../../vis-data/map/svg/seats/Ethiopian_Airlines_Flight_961_seating_plan.svg', // 飞机例子
+                // url: '../../vis-data/map/svg/seats/oracle-seating-map-2017-2.svg', // 渲染错误
+                // url: '../../vis-data/map/svg/seats/DC-10-30_seat_configuration_chart.svg', // 渲染错误
+                // url: '../../vis-data/map/svg/seats/Airbus_A300B4-622R_seat_configuration_chart.svg', // 渲染错误
+                dataType: 'text'
+            }).done(function (svg) {
+
+                echarts.registerMap('seatmap', {
+                    svg: svg
+                });
+
+                option = {
+                    geo: {
+                        map: 'seatmap',
+                        roam: true,
+                        // height: 100,
+                        // zoom: 1.5
+                        emphasis: {
+                            // itemStyle: {
+                            //     color: 'red'
+                            // },
+                            label: {
+                                // color: '#fff',
+                                textBorderColor: '#fff',
+                                textBorderWidth: 2
+                            }
+                        },
+                        // itemStyle: {
+                        //     color: 'red'
+                        // },
+                        // label: {
+                        //     color: '#fff'
+                        // }
+                    },
+                    // series: {
+                    //     type: 'scatter',
+                    //     coordinateSystem: 'geo',
+                    //     // ?????????????????????????
+                    //     geoIndex: 0,
+                    //     data: [[11, 22], [33, 44]]
+                    // }
+                };
+
+                var chart = testHelper.create(echarts, 'main1', {
+                    title: [
+                        'Test Case Description of main0',
+                        '(Muliple lines and **emphasis** are supported in description)'
+                    ],
+                    option: option,
+                    height: 300
+                    // buttons: [{text: 'btn-txt', onclick: function () {}}],
+                    // recordCanvas: true,
+                });
+
+            });
+
+        });
+        </script>
+
+
+
+
+    </body>
+</html>
+


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org


[echarts] 02/06: fix: [geo] clean code and add comment and make interface better.

Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a commit to branch fix/geo-svg
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit 6a22cc65dd0a8553927ca0d2af4c123117fa8c59
Author: 100pah <su...@gmail.com>
AuthorDate: Wed Mar 3 21:00:23 2021 +0800

    fix: [geo] clean code and add comment and make interface better.
---
 src/component/helper/MapDraw.ts | 41 ++++++++-------------------
 src/coord/View.ts               | 61 +++++++++++++++++++++++++++++++----------
 src/coord/geo/Geo.ts            | 14 ++++------
 src/coord/geo/GeoModel.ts       |  5 ++--
 src/coord/geo/geoCreator.ts     |  1 -
 5 files changed, 68 insertions(+), 54 deletions(-)

diff --git a/src/component/helper/MapDraw.ts b/src/component/helper/MapDraw.ts
index 617712a..e1a8f47 100644
--- a/src/component/helper/MapDraw.ts
+++ b/src/component/helper/MapDraw.ts
@@ -35,7 +35,6 @@ import MapView from '../../chart/map/MapView';
 import Region from '../../coord/geo/Region';
 import Geo from '../../coord/geo/Geo';
 import Model from '../../model/Model';
-import Transformable from 'zrender/src/core/Transformable';
 import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle';
 import { getECData } from '../../util/innerStore';
 import { createOrUpdatePatternFromDecal } from '../../util/decal';
@@ -61,7 +60,6 @@ class MapDraw {
 
     private uid: string;
 
-    // @ts-ignore FIXME:TS
     private _controller: RoamController;
 
     private _controllerHost: {
@@ -82,8 +80,6 @@ class MapDraw {
 
     private _mapName: string;
 
-    private _initialized: string;
-
     private _regionsGroup: RegionsGroup;
 
     private _backgroundGroup: graphic.Group;
@@ -92,7 +88,6 @@ class MapDraw {
     constructor(api: ExtensionAPI) {
         const group = new graphic.Group();
         this.uid = getUID('ec_map_draw');
-        // @ts-ignore FIXME:TS
         this._controller = new RoamController(api.getZr());
         this._controllerHost = { target: group };
         this.group = group;
@@ -128,29 +123,21 @@ class MapDraw {
         const group = this.group;
 
         const transformInfo = geo.getTransformInfo();
+        const transformInfoRaw = transformInfo.raw;
+        const transformInfoRoam = transformInfo.roam;
 
         // No animation when first draw or in action
         const isFirstDraw = !regionsGroup.childAt(0) || payload;
-        let targetScaleX: number;
-        let targetScaleY: number;
+
         if (isFirstDraw) {
-            group.transform = transformInfo.roamTransform;
-            group.decomposeTransform();
+            group.x = transformInfoRoam.x;
+            group.y = transformInfoRoam.y;
+            group.scaleX = transformInfoRoam.scaleX;
+            group.scaleY = transformInfoRoam.scaleY;
             group.dirty();
         }
         else {
-            const target = new Transformable();
-            target.transform = transformInfo.roamTransform;
-            target.decomposeTransform();
-            const props = {
-                scaleX: target.scaleX,
-                scaleY: target.scaleY,
-                x: target.x,
-                y: target.y
-            };
-            targetScaleX = target.scaleX;
-            targetScaleY = target.scaleY;
-            graphic.updateProps(group, props, mapOrGeoModel);
+            graphic.updateProps(group, transformInfoRoam, mapOrGeoModel);
         }
 
         regionsGroup.removeAll();
@@ -216,14 +203,10 @@ class MapDraw {
                 }
             }
 
-            const sx = transformInfo.rawScaleX;
-            const sy = transformInfo.rawScaleY;
-            const offsetX = transformInfo.rawX;
-            const offsetY = transformInfo.rawY;
             const transformPoint = function (point: number[]): number[] {
                 return [
-                    point[0] * sx + offsetX,
-                    point[1] * sy + offsetY
+                    point[0] * transformInfoRaw.scaleX + transformInfoRaw.x,
+                    point[1] * transformInfoRaw.scaleY + transformInfoRaw.y
                 ];
             };
 
@@ -333,8 +316,8 @@ class MapDraw {
                 if (!isFirstDraw) {
                     // Text animation
                     graphic.updateProps(textEl, {
-                        scaleX: 1 / targetScaleX,
-                        scaleY: 1 / targetScaleY
+                        scaleX: 1 / transformInfoRoam.scaleX,
+                        scaleY: 1 / transformInfoRoam.scaleY
                     }, mapOrGeoModel);
                 }
             }
diff --git a/src/coord/View.ts b/src/coord/View.ts
index 17bafc8..6a72a3f 100644
--- a/src/coord/View.ts
+++ b/src/coord/View.ts
@@ -22,7 +22,6 @@
  * Mapping given x, y to transformd view x, y
  */
 
-import * as zrUtil from 'zrender/src/core/util';
 import * as vector from 'zrender/src/core/vector';
 import * as matrix from 'zrender/src/core/matrix';
 import BoundingRect from 'zrender/src/core/BoundingRect';
@@ -47,14 +46,37 @@ class View extends Transformable implements CoordinateSystemMaster, CoordinateSy
         min?: number;
     };
 
+    /**
+     * Represents the transform brought by roam/zoom.
+     */
     private _roamTransformable = new Transformable();
+    /**
+     * Represents the transform from `View['_rect']` to `View['_viewRect']`.
+     */
     protected _rawTransformable = new Transformable();
+    private _rawTransform: matrix.MatrixArray;
 
+    /**
+     * This is a user specified point on the source, which will be
+     * located to the center of the `View['_viewRect']`.
+     * The unit this the same as `View['_rect']`.
+     */
     private _center: number[];
     private _zoom: number;
-    protected _rect: BoundingRect;
+
+    /**
+     * The rect of the source, where the measure is used by "data" and "center".
+     * Has nothing to do with roam/zoom.
+     * The unit is defined by the source. For example,
+     * for geo source the unit is lat/lng,
+     * for SVG source the unit is the same as the width/height defined in SVG.
+     */
+    private _rect: BoundingRect;
+    /**
+     * The visible rect on the canvas. Has nothing to do with roam/zoom.
+     * The unit of `View['_viewRect']` is pixel of the canvas.
+     */
     private _viewRect: BoundingRect;
-    private _rawTransform: matrix.MatrixArray;
 
 
     constructor(name?: string) {
@@ -62,7 +84,6 @@ class View extends Transformable implements CoordinateSystemMaster, CoordinateSy
         this.name = name;
     }
 
-    // PENDING to getRect
     setBoundingRect(x: number, y: number, width: number, height: number): BoundingRect {
         this._rect = new BoundingRect(x, y, width, height);
         return this._rect;
@@ -71,7 +92,6 @@ class View extends Transformable implements CoordinateSystemMaster, CoordinateSy
     /**
      * @return {module:zrender/core/BoundingRect}
      */
-    // PENDING to getRect
     getBoundingRect(): BoundingRect {
         return this._rect;
     }
@@ -92,8 +112,10 @@ class View extends Transformable implements CoordinateSystemMaster, CoordinateSy
             new BoundingRect(x, y, width, height)
         );
 
-        // Hint: only works before `this._updateTransform` firstly called.
+        const rawParent = rawTransform.parent;
+        rawTransform.parent = null;
         rawTransform.decomposeTransform();
+        rawTransform.parent = rawParent;
 
         this._updateTransform();
     }
@@ -175,7 +197,8 @@ class View extends Transformable implements CoordinateSystemMaster, CoordinateSy
     }
 
     /**
-     * Update transform from roam and mapLocation
+     * Update transform props on `this` based on the current
+     * `this._roamTransformable` and `this._rawTransformable`.
      */
     protected _updateTransform(): void {
         const roamTransformable = this._roamTransformable;
@@ -195,15 +218,25 @@ class View extends Transformable implements CoordinateSystemMaster, CoordinateSy
         this.decomposeTransform();
     }
 
-    getTransformInfo() {
-        const roamTransform = this._roamTransformable.transform;
+    getTransformInfo(): {
+        roam: Pick<Transformable, 'x' | 'y' | 'scaleX' | 'scaleY'>
+        raw: Pick<Transformable, 'x' | 'y' | 'scaleX' | 'scaleY'>
+    } {
+        const roamTransformable = this._roamTransformable;
         const rawTransformable = this._rawTransformable;
         return {
-            roamTransform: roamTransform ? zrUtil.slice(roamTransform) : matrix.create(),
-            rawScaleX: rawTransformable.scaleX,
-            rawScaleY: rawTransformable.scaleY,
-            rawX: rawTransformable.x,
-            rawY: rawTransformable.y
+            roam: {
+                x: roamTransformable.x,
+                y: roamTransformable.y,
+                scaleX: roamTransformable.scaleX,
+                scaleY: roamTransformable.scaleY
+            },
+            raw: {
+                x: rawTransformable.x,
+                y: rawTransformable.y,
+                scaleX: rawTransformable.scaleX,
+                scaleY: rawTransformable.scaleY
+            }
         };
     }
 
diff --git a/src/coord/geo/Geo.ts b/src/coord/geo/Geo.ts
index 85a3954..516a971 100644
--- a/src/coord/geo/Geo.ts
+++ b/src/coord/geo/Geo.ts
@@ -66,7 +66,9 @@ class Geo extends View {
         this._regionsMap = source.regionsMap;
         this._invertLongitute = invertLongitute == null ? true : invertLongitute;
         this.regions = source.regions;
-        this._rect = source.boundingRect;
+
+        const boundingRect = source.boundingRect;
+        this.setBoundingRect(boundingRect.x, boundingRect.y, boundingRect.width, boundingRect.height);
     }
 
     /**
@@ -99,15 +101,15 @@ class Geo extends View {
             new BoundingRect(x, y, width, height)
         );
 
-        // Hint: only works before `this._updateTransform` firstly called.
+        const rawParent = rawTransformable.parent;
+        rawTransformable.parent = null;
         rawTransformable.decomposeTransform();
+        rawTransformable.parent = rawParent;
 
         if (invertLongitute) {
             rawTransformable.scaleY = -rawTransformable.scaleY;
         }
 
-        rawTransformable.updateTransform();
-
         this._updateTransform();
     }
 
@@ -138,10 +140,6 @@ class Geo extends View {
         return this._nameCoordMap.get(name);
     }
 
-    getBoundingRect(): BoundingRect {
-        return this._rect;
-    }
-
     dataToPoint(data: number[], noRoam?: boolean, out?: number[]): number[] {
         if (typeof data === 'string') {
             // Map area name to geoCoord
diff --git a/src/coord/geo/GeoModel.ts b/src/coord/geo/GeoModel.ts
index 16c14bb..3705304 100644
--- a/src/coord/geo/GeoModel.ts
+++ b/src/coord/geo/GeoModel.ts
@@ -128,8 +128,9 @@ class GeoModel extends ComponentModel<GeoOption> {
 
         top: 'center',
 
-        // If svg used, aspectScale is 1 by default.
-        // aspectScale: 0.75,
+        // Default value:
+        // for SVG source: 1,
+        // for geoJSON source: 0.75.
         aspectScale: null,
 
         ///// Layout with center and size
diff --git a/src/coord/geo/geoCreator.ts b/src/coord/geo/geoCreator.ts
index 47ad2cf..fad9400 100644
--- a/src/coord/geo/geoCreator.ts
+++ b/src/coord/geo/geoCreator.ts
@@ -105,7 +105,6 @@ function resizeGeo(this: Geo, geoModel: ComponentModel<GeoOption | MapSeriesOpti
         // Use left/top/width/height
         const boxLayoutOption = geoModel.getBoxLayoutParams() as Parameters<typeof layout.getLayoutRect>[0];
 
-        // 0.75 rate
         boxLayoutOption.aspect = aspect;
 
         viewRect = layout.getLayoutRect(boxLayoutOption, {


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org


[echarts] 03/06: fix: [geo] (1) refactor of geoJSON & SVG source loader and management. (2) remove the support of "load multiple geoJSON or SVG in one map name". This feature is never documented and published. And this feature have a inherent problem that it's not easy to normalize the unit among the different SVG. After this commit, one map name can only have one geoJSON or one SVG.

Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a commit to branch fix/geo-svg
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit e9af050807d890ed99ddf6ecf8fa891993ca5e22
Author: 100pah <su...@gmail.com>
AuthorDate: Sun Mar 7 01:06:13 2021 +0800

    fix: [geo]
    (1) refactor of geoJSON & SVG source loader and management.
    (2) remove the support of "load multiple geoJSON or SVG in one map name". This feature is never documented and published. And this feature have a inherent problem that it's not easy to normalize the unit among the different SVG. After this commit, one map name can only have one geoJSON or one SVG.
---
 src/component/helper/MapDraw.ts                    |  50 +++--
 src/coord/View.ts                                  |   6 +-
 src/coord/geo/GeoJSONResource.ts                   | 166 ++++++++++++++++
 .../geo/{geoSVGLoader.ts => GeoSVGResource.ts}     | 115 ++++++-----
 src/coord/geo/geoCreator.ts                        |   6 +-
 src/coord/geo/geoJSONLoader.ts                     |  99 ----------
 src/coord/geo/geoSourceManager.ts                  | 217 +++++++++++----------
 src/coord/geo/geoTypes.ts                          |  19 ++
 src/coord/geo/mapDataStorage.ts                    | 158 ---------------
 src/core/echarts.ts                                |  21 +-
 10 files changed, 416 insertions(+), 441 deletions(-)

diff --git a/src/component/helper/MapDraw.ts b/src/component/helper/MapDraw.ts
index e1a8f47..79bc1cc 100644
--- a/src/component/helper/MapDraw.ts
+++ b/src/component/helper/MapDraw.ts
@@ -38,10 +38,12 @@ import Model from '../../model/Model';
 import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle';
 import { getECData } from '../../util/innerStore';
 import { createOrUpdatePatternFromDecal } from '../../util/decal';
+import { ViewCoordSysTransformInfoPart } from '../../coord/View';
+import { GeoSVGResource } from '../../coord/geo/GeoSVGResource';
 
 
 interface RegionsGroup extends graphic.Group {
-    __regions: Region[];
+    __regions: Region[]
 }
 
 function getFixedItemStyle(model: Model<GeoItemStyleOption>) {
@@ -82,7 +84,7 @@ class MapDraw {
 
     private _regionsGroup: RegionsGroup;
 
-    private _backgroundGroup: graphic.Group;
+    private _svgGroup: graphic.Group;
 
 
     constructor(api: ExtensionAPI) {
@@ -93,7 +95,7 @@ class MapDraw {
         this.group = group;
 
         group.add(this._regionsGroup = new graphic.Group() as RegionsGroup);
-        group.add(this._backgroundGroup = new graphic.Group());
+        group.add(this._svgGroup = new graphic.Group());
     }
 
     draw(
@@ -117,7 +119,6 @@ class MapDraw {
 
         const geo = mapOrGeoModel.coordinateSystem;
 
-        this._updateBackground(geo);
 
         const regionsGroup = this._regionsGroup;
         const group = this.group;
@@ -126,6 +127,8 @@ class MapDraw {
         const transformInfoRaw = transformInfo.raw;
         const transformInfoRoam = transformInfo.roam;
 
+        this._updateSVG(geo, transformInfoRaw);
+
         // No animation when first draw or in action
         const isFirstDraw = !regionsGroup.childAt(0) || payload;
 
@@ -356,23 +359,48 @@ class MapDraw {
 
     remove(): void {
         this._regionsGroup.removeAll();
-        this._backgroundGroup.removeAll();
+        this._svgGroup.removeAll();
         this._controller.dispose();
-        this._mapName && geoSourceManager.removeGraphic(this._mapName, this.uid);
+        this._freeSVG(this._mapName);
         this._mapName = null;
         this._controllerHost = null;
     }
 
-    private _updateBackground(geo: Geo): void {
+    private _updateSVG(geo: Geo, transformInfoRaw: ViewCoordSysTransformInfoPart): void {
         const mapName = geo.map;
 
+        this._svgGroup.x = transformInfoRaw.x;
+        this._svgGroup.y = transformInfoRaw.y;
+        this._svgGroup.scaleX = transformInfoRaw.scaleX;
+        this._svgGroup.scaleY = transformInfoRaw.scaleY;
+
         if (this._mapName !== mapName) {
-            zrUtil.each(geoSourceManager.makeGraphic(mapName, this.uid), function (root) {
-                this._backgroundGroup.add(root);
-            }, this);
+            this._freeSVG(this._mapName);
+            this._useSVG(mapName);
+            this._mapName = mapName;
         }
+    }
 
-        this._mapName = mapName;
+    private _useSVG(mapName: string) {
+        if (mapName == null) {
+            return;
+        }
+        const resource = geoSourceManager.getGeoResource(mapName);
+        if (resource && resource.type === 'svg') {
+            const root = (resource as GeoSVGResource).useGraphic(this.uid);
+            this._svgGroup.add(root);
+        }
+    }
+
+    private _freeSVG(mapName: string) {
+        if (mapName == null) {
+            return;
+        }
+        const resource = geoSourceManager.getGeoResource(mapName);
+        if (resource && resource.type === 'svg') {
+            (resource as GeoSVGResource).freeGraphic(this.uid);
+            this._svgGroup.removeAll();
+        }
     }
 
     private _updateController(
diff --git a/src/coord/View.ts b/src/coord/View.ts
index 6a72a3f..93fa411 100644
--- a/src/coord/View.ts
+++ b/src/coord/View.ts
@@ -32,6 +32,8 @@ import { ParsedModelFinder, ParsedModelFinderKnown } from '../util/model';
 
 const v2ApplyTransform = vector.applyTransform;
 
+export type ViewCoordSysTransformInfoPart = Pick<Transformable, 'x' | 'y' | 'scaleX' | 'scaleY'>;
+
 class View extends Transformable implements CoordinateSystemMaster, CoordinateSystem {
 
     readonly type: string = 'view';
@@ -219,8 +221,8 @@ class View extends Transformable implements CoordinateSystemMaster, CoordinateSy
     }
 
     getTransformInfo(): {
-        roam: Pick<Transformable, 'x' | 'y' | 'scaleX' | 'scaleY'>
-        raw: Pick<Transformable, 'x' | 'y' | 'scaleX' | 'scaleY'>
+        roam: ViewCoordSysTransformInfoPart
+        raw: ViewCoordSysTransformInfoPart
     } {
         const roamTransformable = this._roamTransformable;
         const rawTransformable = this._rawTransformable;
diff --git a/src/coord/geo/GeoJSONResource.ts b/src/coord/geo/GeoJSONResource.ts
new file mode 100644
index 0000000..6189322
--- /dev/null
+++ b/src/coord/geo/GeoJSONResource.ts
@@ -0,0 +1,166 @@
+/*
+* 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 Region from './Region';
+import { GeoJSON, GeoJSONCompressed, GeoJSONSourceInput, GeoResource, GeoSpecialAreas, NameMap } from './geoTypes';
+
+
+export class GeoJSONResource implements GeoResource {
+
+    readonly type = 'geoJSON';
+    private _geoJSON: GeoJSON | GeoJSONCompressed;
+    private _specialAreas: GeoSpecialAreas;
+    private _mapName: string;
+
+    private _parsed: {
+        regions: Region[];
+        boundingRect: BoundingRect;
+    };
+
+    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);
+    }
+
+    load(nameMap: NameMap, nameProperty: string) {
+
+        let parsed = this._parsed;
+        if (!parsed) {
+            const rawRegions = this._parseToRegions(nameProperty);
+            parsed = this._parsed = {
+                regions: rawRegions,
+                boundingRect: calculateBoundingRect(rawRegions)
+            };
+        }
+
+        const regionsMap = createHashMap<Region>();
+        const nameCoordMap = createHashMap<Region['center']>();
+
+        const finalRegions: Region[] = [];
+        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);
+            nameCoordMap.set(regionName, region.center);
+        });
+
+        return {
+            regions: finalRegions,
+            boundingRect: parsed.boundingRect || new BoundingRect(0, 0, 0, 0),
+            regionsMap: regionsMap,
+            nameCoordMap: nameCoordMap
+        };
+    }
+
+    private _parseToRegions(nameProperty: string): Region[] {
+        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: Region[]): 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 + ');'))();
+}
diff --git a/src/coord/geo/geoSVGLoader.ts b/src/coord/geo/GeoSVGResource.ts
similarity index 50%
rename from src/coord/geo/geoSVGLoader.ts
rename to src/coord/geo/GeoSVGResource.ts
index 349dbb7..14a29c3 100644
--- a/src/coord/geo/geoSVGLoader.ts
+++ b/src/coord/geo/GeoSVGResource.ts
@@ -22,82 +22,95 @@ import Group from 'zrender/src/graphic/Group';
 import Rect from 'zrender/src/graphic/shape/Rect';
 import {assert, createHashMap, HashMap} from 'zrender/src/core/util';
 import BoundingRect from 'zrender/src/core/BoundingRect';
-import {makeInner} from '../../util/model';
-import { SVGMapRecord } from './mapDataStorage';
+import { GeoResource, GeoSVGSourceInput } from './geoTypes';
+import { parseXML } from 'zrender/src/tool/parseXML';
 
-type MapRecordInner = {
-    originRoot: Group;
-    boundingRect: BoundingRect;
-    // key: hostKey, value: root
-    rootMap: HashMap<Group>;
-    originRootHostKey: string;
-};
 
-const inner = makeInner<MapRecordInner, SVGMapRecord>();
+export class GeoSVGResource implements GeoResource {
 
-export default {
+    readonly type = 'svg';
+    private _mapName: string;
+    private _parsedXML: SVGElement;
+    private _rootForRect: Group;
+    private _boundingRect: BoundingRect;
+    // key: hostKey, value: root
+    private _usedRootMap: HashMap<Group> = createHashMap();
+    private _freedRoots: Group[] = [];
+
+    constructor(
+        mapName: string,
+        svg: GeoSVGSourceInput
+    ) {
+        this._mapName = mapName;
+
+        // Only perform parse to XML object here, which might be time
+        // consiming for large SVG.
+        // Although convert XML to zrender element is also time consiming,
+        // if we do it here, the clone of zrender elements has to be
+        // required. So we do it once for each geo instance, util real
+        // performance issues call for optimizing it.
+        this._parsedXML = parseXML(svg);
+    }
 
-    load(mapName: string, mapRecord: SVGMapRecord): ReturnType<typeof buildGraphic> {
-        const originRoot = inner(mapRecord).originRoot;
-        if (originRoot) {
-            return {
-                root: originRoot,
-                boundingRect: inner(mapRecord).boundingRect
-            };
+    load(): { boundingRect: BoundingRect } {
+        // In the "load" stage, graphic need to be built to
+        // get boundingRect for geo coordinate system.
+        const rootForRect = this._rootForRect;
+        if (rootForRect) {
+            return { boundingRect: this._boundingRect };
         }
 
-        const graphic = buildGraphic(mapRecord);
+        const graphic = buildGraphic(this._parsedXML);
 
-        inner(mapRecord).originRoot = graphic.root;
-        inner(mapRecord).boundingRect = graphic.boundingRect;
+        this._rootForRect = graphic.root;
+        this._boundingRect = graphic.boundingRect;
 
-        return graphic;
-    },
+        this._freedRoots.push(graphic.root);
 
-    makeGraphic(mapName: string, mapRecord: SVGMapRecord, hostKey: string): Group {
-        // For performance consideration (in large SVG), graphic only maked
-        // when necessary and reuse them according to hostKey.
-        const field = inner(mapRecord);
-        const rootMap = field.rootMap || (field.rootMap = createHashMap());
+        return { boundingRect: graphic.boundingRect };
+    }
 
-        let root = rootMap.get(hostKey);
+    // Consider:
+    // (1) One graphic element can not be shared by different `geoView` running simultaneously.
+    //     Notice, also need to consider multiple echarts instances share a `mapRecord`.
+    // (2) Converting SVG to graphic elements is time consuming.
+    // (3) In the current architecture, `load` should be called frequently to get boundingRect,
+    //     and it is called without view info.
+    // So we maintain graphic elements in this module, and enables `view` to use/return these
+    // graphics from/to the pool with it's uid.
+    useGraphic(hostKey: string): Group {
+        const usedRootMap = this._usedRootMap;
+
+        let root = usedRootMap.get(hostKey);
         if (root) {
             return root;
         }
 
-        const originRoot = field.originRoot;
-        const boundingRect = field.boundingRect;
+        root = this._freedRoots.pop() || buildGraphic(this._parsedXML, this._boundingRect).root;
 
-        // For performance, if originRoot is not used by a view,
-        // assign it to a view, but not reproduce graphic elements.
-        if (!field.originRootHostKey) {
-            field.originRootHostKey = hostKey;
-            root = originRoot;
-        }
-        else {
-            root = buildGraphic(mapRecord, boundingRect).root;
-        }
+        return usedRootMap.set(hostKey, root);
+    }
 
-        return rootMap.set(hostKey, root);
-    },
+    freeGraphic(hostKey: string): void {
+        const usedRootMap = this._usedRootMap;
 
-    removeGraphic(mapName: string, mapRecord: SVGMapRecord, hostKey: string): void {
-        const field = inner(mapRecord);
-        const rootMap = field.rootMap;
-        rootMap && rootMap.removeKey(hostKey);
-        if (hostKey === field.originRootHostKey) {
-            field.originRootHostKey = null;
+        const root = usedRootMap.get(hostKey);
+        if (root) {
+            usedRootMap.removeKey(hostKey);
+            this._freedRoots.push(root);
         }
     }
-};
+
+}
+
 
 function buildGraphic(
-    mapRecord: SVGMapRecord, boundingRect?: BoundingRect
+    svgXML: SVGElement,
+    boundingRect?: BoundingRect
 ): {
     root: Group;
     boundingRect: BoundingRect;
 } {
-    const svgXML = mapRecord.svgXML;
     let result;
     let root;
 
diff --git a/src/coord/geo/geoCreator.ts b/src/coord/geo/geoCreator.ts
index fad9400..e01f0d8 100644
--- a/src/coord/geo/geoCreator.ts
+++ b/src/coord/geo/geoCreator.ts
@@ -22,7 +22,6 @@ import Geo from './Geo';
 import * as layout from '../../util/layout';
 import * as numberUtil from '../../util/number';
 import geoSourceManager from './geoSourceManager';
-import mapDataStorage from './mapDataStorage';
 import GeoModel, { GeoOption, RegoinOption } from './GeoModel';
 import MapSeries, { MapSeriesOption } from '../../chart/map/MapSeries';
 import ExtensionAPI from '../../core/ExtensionAPI';
@@ -141,8 +140,9 @@ class GeoCreator implements CoordinateSystemCreator {
 
             let aspectScale = geoModel.get('aspectScale');
             let invertLongitute = true;
-            const mapRecords = mapDataStorage.retrieveMap(name);
-            if (mapRecords && mapRecords[0] && mapRecords[0].type === 'svg') {
+
+            const geoResource = geoSourceManager.getGeoResource(name);
+            if (geoResource.type === 'svg') {
                 aspectScale == null && (aspectScale = 1);
                 invertLongitute = false;
             }
diff --git a/src/coord/geo/geoJSONLoader.ts b/src/coord/geo/geoJSONLoader.ts
deleted file mode 100644
index 93e5c9e..0000000
--- a/src/coord/geo/geoJSONLoader.ts
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
-* 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} from 'zrender/src/core/util';
-import parseGeoJson from './parseGeoJson';
-import {makeInner} from '../../util/model';
-
-// 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 { GeoJSONMapRecord } from './mapDataStorage';
-import BoundingRect from 'zrender/src/core/BoundingRect';
-import Region from './Region';
-
-type MapRecordInner = {
-    parsed: {
-        regions: Region[];
-        boundingRect: BoundingRect;
-    };
-};
-
-const inner = makeInner<MapRecordInner, GeoJSONMapRecord>();
-
-export default {
-
-    load(mapName: string, mapRecord: GeoJSONMapRecord, nameProperty: string): MapRecordInner['parsed'] {
-
-        const parsed = inner(mapRecord).parsed;
-
-        if (parsed) {
-            return parsed;
-        }
-
-        const specialAreas = mapRecord.specialAreas || {};
-        const geoJSON = mapRecord.geoJSON;
-        let regions;
-
-        // https://jsperf.com/try-catch-performance-overhead
-        try {
-            regions = geoJSON ? parseGeoJson(geoJSON, nameProperty) : [];
-        }
-        catch (e) {
-            throw new Error('Invalid geoJson format\n' + e.message);
-        }
-
-        fixNanhai(mapName, regions);
-
-        each(regions, 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 = specialAreas[regionName];
-            if (specialArea) {
-                region.transformTo(
-                    specialArea.left, specialArea.top, specialArea.width, specialArea.height
-                );
-            }
-        });
-
-        return (inner(mapRecord).parsed = {
-            regions: regions,
-            boundingRect: getBoundingRect(regions)
-        });
-    }
-};
-
-function getBoundingRect(regions: Region[]): 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;
-}
-
diff --git a/src/coord/geo/geoSourceManager.ts b/src/coord/geo/geoSourceManager.ts
index e324bbf..66cdf1e 100644
--- a/src/coord/geo/geoSourceManager.ts
+++ b/src/coord/geo/geoSourceManager.ts
@@ -17,122 +17,133 @@
 * under the License.
 */
 
-import {each, createHashMap, HashMap} from 'zrender/src/core/util';
-import mapDataStorage, { MapRecord } from './mapDataStorage';
-import geoJSONLoader from './geoJSONLoader';
-import geoSVGLoader from './geoSVGLoader';
-import BoundingRect from 'zrender/src/core/BoundingRect';
-import { NameMap } from './geoTypes';
-import Region from './Region';
-import { Dictionary } from 'zrender/src/core/types';
-import Group from 'zrender/src/graphic/Group';
-
-
-interface Loader {
-    load: (mapName: string, mapRecord: MapRecord, nameProperty?: string) => {
-        regions?: Region[];
-        boundingRect?: BoundingRect;
-    };
-    makeGraphic?: (mapName: string, mapRecord: MapRecord, hostKey: string) => Group;
-    removeGraphic?: (mapName: string, mapRecord: MapRecord, hostKey: string) => void;
+import { createHashMap } from 'zrender/src/core/util';
+import { GeoSVGResource } from './GeoSVGResource';
+import {
+    GeoJSON,
+    GeoJSONSourceInput,
+    GeoResource,
+    GeoSpecialAreas,
+    NameMap,
+    GeoSVGSourceInput
+} from './geoTypes';
+import { GeoJSONResource } from './GeoJSONResource';
+
+
+type MapInput = GeoJSONMapInput | SVGMapInput;
+interface GeoJSONMapInput {
+    geoJSON: GeoJSONSourceInput;
+    specialAreas: GeoSpecialAreas;
 }
-const loaders = {
-    geoJSON: geoJSONLoader,
-    svg: geoSVGLoader
-} as Dictionary<Loader>;
+interface GeoJSONMapInputCompat extends GeoJSONMapInput {
+    geoJson: GeoJSONSourceInput;
+}
+interface SVGMapInput {
+    svg: GeoSVGSourceInput;
+}
+
+
+const storage = createHashMap<GeoResource>();
+
 
 export default {
 
-    load: function (mapName: string, nameMap: NameMap, nameProperty?: string): {
-        regions: Region[];
-        // Key: mapName
-        regionsMap: HashMap<Region>;
-        // Key: mapName
-        nameCoordMap: HashMap<number[]>;
-        boundingRect: BoundingRect
-    } {
-        const regions = [] as Region[];
-        const regionsMap = createHashMap<Region>();
-        const nameCoordMap = createHashMap<Region['center']>();
-        let boundingRect: BoundingRect;
-        const mapRecords = retrieveMap(mapName);
-
-        each(mapRecords, function (record) {
-            const singleSource = loaders[record.type].load(mapName, record, nameProperty);
-
-            each(singleSource.regions, function (region) {
-                let regionName = region.name;
-
-                // Try use the alias in geoNameMap
-                if (nameMap && nameMap.hasOwnProperty(regionName)) {
-                    region = region.cloneShallow(regionName = nameMap[regionName]);
-                }
-
-                regions.push(region);
-                regionsMap.set(regionName, region);
-                nameCoordMap.set(regionName, region.center);
-            });
-
-            const rect = singleSource.boundingRect;
-            if (rect) {
-                boundingRect
-                    ? boundingRect.union(rect)
-                    : (boundingRect = rect.clone());
+    /**
+     * Compatible with previous `echarts.registerMap`.
+     *
+     * @usage
+     * ```js
+     *
+     * echarts.registerMap('USA', geoJson, specialAreas);
+     *
+     * echarts.registerMap('USA', {
+     *     geoJson: geoJson,
+     *     specialAreas: {...}
+     * });
+     * echarts.registerMap('USA', {
+     *     geoJSON: geoJson,
+     *     specialAreas: {...}
+     * });
+     *
+     * echarts.registerMap('airport', {
+     *     svg: svg
+     * }
+     * ```
+     *
+     * Note:
+     * Do not support that register multiple geoJSON or SVG
+     * one map name. Because different geoJSON and SVG have
+     * different unit. It's not easy to make sure how those
+     * units are mapping/normalize.
+     * If intending to use multiple geoJSON or SVG, we can
+     * use multiple geo coordinate system.
+     */
+    registerMap: function (
+        mapName: string,
+        rawDef: MapInput | GeoJSONSourceInput,
+        rawSpecialAreas?: GeoSpecialAreas
+    ): void {
+
+        if ((rawDef as SVGMapInput).svg) {
+            const resource = new GeoSVGResource(
+                mapName,
+                (rawDef as SVGMapInput).svg
+            );
+
+            storage.set(mapName, resource);
+        }
+        else {
+            // Recommend:
+            //     echarts.registerMap('eu', { geoJSON: xxx, specialAreas: xxx });
+            // Backward compatibility:
+            //     echarts.registerMap('eu', geoJSON, specialAreas);
+            //     echarts.registerMap('eu', { geoJson: xxx, specialAreas: xxx });
+            let geoJSON = (rawDef as GeoJSONMapInputCompat).geoJson
+                || (rawDef as GeoJSONMapInput).geoJSON;
+            if (geoJSON && !(rawDef as GeoJSON).features) {
+                rawSpecialAreas = (rawDef as GeoJSONMapInput).specialAreas;
+            }
+            else {
+                geoJSON = rawDef as GeoJSONSourceInput;
             }
-        });
-
-        return {
-            regions: regions,
-            regionsMap: regionsMap,
-            nameCoordMap: nameCoordMap,
-            // FIXME Always return new ?
-            boundingRect: boundingRect || new BoundingRect(0, 0, 0, 0)
-        };
+            const resource = new GeoJSONResource(
+                mapName,
+                geoJSON,
+                rawSpecialAreas
+            );
+
+            storage.set(mapName, resource);
+        }
     },
 
-    /**
-     * @param hostKey For cache.
-     * @return Roots.
-     */
-    makeGraphic: function (mapName: string, hostKey: string): Group[] {
-        const mapRecords = retrieveMap(mapName);
-        const results = [] as Group[];
-        each(mapRecords, function (record) {
-            const method = loaders[record.type].makeGraphic;
-            method && results.push(method(mapName, record, hostKey));
-        });
-        return results;
+    getGeoResource(mapName: string): GeoResource {
+        return storage.get(mapName);
     },
 
     /**
-     * @param hostKey For cache.
+     * Only for exporting to users.
+     * **MUST NOT** used internally.
      */
-    removeGraphic: function (mapName: string, hostKey: string): void {
-        const mapRecords = retrieveMap(mapName);
-        each(mapRecords, function (record) {
-            const method = loaders[record.type].makeGraphic;
-            method && method(mapName, record, hostKey);
-        });
-    }
-};
-
-function mapNotExistsError(mapName: string): void {
-    if (__DEV__) {
-        console.error(
-            'Map ' + mapName + ' not exists. The GeoJSON of the map must be provided.'
-        );
-    }
-}
+    getMapForUser: function (mapName: string): ReturnType<GeoJSONResource['getMapForUser']> {
+        const resource = storage.get(mapName);
+        // Do not support return SVG until some real requirement come.
+        return resource && resource.type === 'geoJSON'
+            && (resource as GeoJSONResource).getMapForUser();
+    },
 
-function retrieveMap(mapName: string): MapRecord[] {
-    const mapRecords = mapDataStorage.retrieveMap(mapName) || [];
+    load: function (mapName: string, nameMap: NameMap, nameProperty?: string) {
+        const resource = storage.get(mapName);
 
-    if (__DEV__) {
-        if (!mapRecords.length) {
-            mapNotExistsError(mapName);
+        if (!resource) {
+            if (__DEV__) {
+                console.error(
+                    'Map ' + mapName + ' not exists. The GeoJSON of the map must be provided.'
+                );
+            }
+            return;
         }
-    }
 
-    return mapRecords;
-}
+        return resource.load(nameMap, nameProperty);
+    }
 
+};
diff --git a/src/coord/geo/geoTypes.ts b/src/coord/geo/geoTypes.ts
index 910e0fe..52b8eb2 100644
--- a/src/coord/geo/geoTypes.ts
+++ b/src/coord/geo/geoTypes.ts
@@ -17,6 +17,13 @@
 * under the License.
 */
 
+import BoundingRect from 'zrender/src/core/BoundingRect';
+import { HashMap } from 'zrender/src/core/util';
+import Region from './Region';
+
+
+export type GeoSVGSourceInput = 'string' | Document | SVGElement;
+export type GeoJSONSourceInput = 'string' | GeoJSON | GeoJSONCompressed;
 
 export interface NameMap {
     [regionName: string]: string
@@ -114,3 +121,15 @@ interface GeoJSONGeometryMultiPolygonCompressed {
 //      type: 'GeometryCollection';
 //      geometries: GeoJSONGeometry[];
 // };
+
+export interface GeoResource {
+    readonly type: 'geoJSON' | 'svg';
+    load(nameMap: NameMap, nameProperty: string): {
+        boundingRect: BoundingRect;
+        regions?: Region[];
+        // Key: mapName
+        regionsMap?: HashMap<Region>;
+        // Key: mapName
+        nameCoordMap?: HashMap<number[]>;
+    };
+}
diff --git a/src/coord/geo/mapDataStorage.ts b/src/coord/geo/mapDataStorage.ts
deleted file mode 100644
index b9562f9..0000000
--- a/src/coord/geo/mapDataStorage.ts
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
-* 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 {createHashMap, isString, isArray, each, assert} from 'zrender/src/core/util';
-import {parseXML} from 'zrender/src/tool/parseXML';
-import { GeoSpecialAreas, GeoJSON, GeoJSONCompressed } from './geoTypes';
-import { Dictionary } from 'zrender/src/core/types';
-
-// For minimize the code size of common echarts package,
-// do not put too much logic in this module.
-
-type SVGMapSource = 'string' | Document | SVGElement;
-type GeoJSONMapSource = 'string' | GeoJSON | GeoJSONCompressed;
-type MapInputObject = {
-    geoJSON?: GeoJSONMapSource;
-    geoJson?: GeoJSONMapSource;
-    svg?: SVGMapSource;
-    specialAreas?: GeoSpecialAreas;
-};
-
-export type MapRecord = GeoJSONMapRecord | SVGMapRecord;
-export interface GeoJSONMapRecord {
-    type: 'geoJSON';
-    source: GeoJSONMapSource;
-    specialAreas: GeoSpecialAreas;
-    geoJSON: GeoJSON | GeoJSONCompressed;
-}
-export interface SVGMapRecord {
-    type: 'svg';
-    source: SVGMapSource;
-    specialAreas: GeoSpecialAreas;
-    svgXML: ReturnType<typeof parseXML>;
-}
-
-
-const storage = createHashMap<MapRecord[]>();
-
-
-export default {
-
-    /**
-     * Compatible with previous `echarts.registerMap`.
-     * @usage
-     * ```js
-     * $.get('USA.json', function (geoJson) {
-     *     echarts.registerMap('USA', geoJson);
-     *     // Or
-     *     echarts.registerMap('USA', {
-     *         geoJson: geoJson,
-     *         specialAreas: {}
-     *     })
-     * });
-     *
-     * $.get('airport.svg', function (svg) {
-     *     echarts.registerMap('airport', {
-     *         svg: svg
-     *     }
-     * });
-     *
-     * echarts.registerMap('eu', [
-     *     {svg: eu-topographic.svg},
-     *     {geoJSON: eu.json}
-     * ])
-     * ```
-     */
-    registerMap: function (
-        mapName: string,
-        rawDef: MapInputObject | MapRecord[] | GeoJSONMapSource,
-        rawSpecialAreas?: GeoSpecialAreas
-    ): MapRecord[] {
-
-        let records: MapRecord[];
-
-        if (isArray(rawDef)) {
-            records = rawDef as MapRecord[];
-        }
-        else if ((rawDef as MapInputObject).svg) {
-            records = [{
-                type: 'svg',
-                source: (rawDef as MapInputObject).svg,
-                specialAreas: (rawDef as MapInputObject).specialAreas
-            } as SVGMapRecord];
-        }
-        else {
-            // Backward compatibility.
-            const geoSource = (rawDef as MapInputObject).geoJson
-                || (rawDef as MapInputObject).geoJSON;
-            if (geoSource && !(rawDef as GeoJSON).features) {
-                rawSpecialAreas = (rawDef as MapInputObject).specialAreas;
-                rawDef = geoSource;
-            }
-            records = [{
-                type: 'geoJSON',
-                source: rawDef as GeoJSONMapSource,
-                specialAreas: rawSpecialAreas
-            } as GeoJSONMapRecord];
-        }
-
-        each(records, function (record) {
-            let type = record.type;
-            (type as any) === 'geoJson' && (type = record.type = 'geoJSON');
-
-            const parse = parsers[type];
-
-            if (__DEV__) {
-                assert(parse, 'Illegal map type: ' + type);
-            }
-
-            parse(record);
-        });
-
-        return storage.set(mapName, records);
-    },
-
-    retrieveMap: function (mapName: string): MapRecord[] {
-        return storage.get(mapName);
-    }
-
-};
-
-const parsers: Dictionary<(record: MapRecord) => void> = {
-
-    geoJSON: function (record: GeoJSONMapRecord): void {
-        const source = record.source;
-        record.geoJSON = !isString(source)
-            ? source
-            : (typeof JSON !== 'undefined' && JSON.parse)
-            ? JSON.parse(source)
-            : (new Function('return (' + source + ');'))();
-    },
-
-    // Only perform parse to XML object here, which might be time
-    // consiming for large SVG.
-    // Although convert XML to zrender element is also time consiming,
-    // if we do it here, the clone of zrender elements has to be
-    // required. So we do it once for each geo instance, util real
-    // performance issues call for optimizing it.
-    svg: function (record: SVGMapRecord): void {
-        record.svgXML = parseXML(record.source as SVGMapSource);
-    }
-
-};
diff --git a/src/core/echarts.ts b/src/core/echarts.ts
index 20cf0f2..6bf60e8 100644
--- a/src/core/echarts.ts
+++ b/src/core/echarts.ts
@@ -68,7 +68,6 @@ import loadingDefault from '../loading/default';
 import Scheduler from './Scheduler';
 import lightTheme from '../theme/light';
 import darkTheme from '../theme/dark';
-import mapDataStorage from '../coord/geo/mapDataStorage';
 import {CoordinateSystemMaster, CoordinateSystemCreator, CoordinateSystemHostModel} from '../coord/CoordinateSystem';
 import { parseClassType } from '../util/clazz';
 import {ECEventProcessor} from '../util/ECEventProcessor';
@@ -105,6 +104,7 @@ import decal from '../visual/decal';
 import type {MorphDividingMethod} from 'zrender/src/tool/morphPath';
 import CanvasPainter from 'zrender/src/canvas/Painter';
 import SVGPainter from 'zrender/src/svg/Painter';
+import geoSourceManager from '../coord/geo/geoSourceManager';
 
 declare let global: any;
 
@@ -2831,26 +2831,19 @@ export function setCanvasCreator(creator: () => HTMLCanvasElement): void {
 }
 
 /**
- * The parameters and usage: see `mapDataStorage.registerMap`.
+ * The parameters and usage: see `geoSourceManager.registerMap`.
  * Compatible with previous `echarts.registerMap`.
  */
 export function registerMap(
-    mapName: Parameters<typeof mapDataStorage.registerMap>[0],
-    geoJson: Parameters<typeof mapDataStorage.registerMap>[1],
-    specialAreas?: Parameters<typeof mapDataStorage.registerMap>[2]
+    mapName: Parameters<typeof geoSourceManager.registerMap>[0],
+    geoJson: Parameters<typeof geoSourceManager.registerMap>[1],
+    specialAreas?: Parameters<typeof geoSourceManager.registerMap>[2]
 ): void {
-    mapDataStorage.registerMap(mapName, geoJson, specialAreas);
+    geoSourceManager.registerMap(mapName, geoJson, specialAreas);
 }
 
 export function getMap(mapName: string) {
-    // For backward compatibility, only return the first one.
-    const records = mapDataStorage.retrieveMap(mapName);
-    // FIXME support SVG, where return not only records[0].
-    return records && records[0] && {
-        // @ts-ignore
-        geoJson: records[0].geoJSON,
-        specialAreas: records[0].specialAreas
-    };
+    return geoSourceManager.getMapForUser(mapName);
 }
 
 export const registerTransform = registerExternalTransform;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org


[echarts] 04/06: fix: [geo] (1) label scale implementation change: user afterUpdate rather than / parentScale. (2) some refactor and clean code.

Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a commit to branch fix/geo-svg
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit 6b5a0e505171e605a80688b95097b54646f38d15
Author: 100pah <su...@gmail.com>
AuthorDate: Mon Mar 8 17:33:57 2021 +0800

    fix: [geo]
    (1) label scale implementation change: user afterUpdate rather than / parentScale.
    (2) some refactor and clean code.
---
 src/component/helper/MapDraw.ts | 47 ++++++++++++++++-------------------------
 src/coord/geo/GeoSVGResource.ts | 37 +++++++++++++++++++-------------
 2 files changed, 40 insertions(+), 44 deletions(-)

diff --git a/src/component/helper/MapDraw.ts b/src/component/helper/MapDraw.ts
index 79bc1cc..1ca9ad9 100644
--- a/src/component/helper/MapDraw.ts
+++ b/src/component/helper/MapDraw.ts
@@ -32,7 +32,6 @@ import GlobalModel from '../../model/Global';
 import { Payload, ECElement } from '../../util/types';
 import GeoView from '../geo/GeoView';
 import MapView from '../../chart/map/MapView';
-import Region from '../../coord/geo/Region';
 import Geo from '../../coord/geo/Geo';
 import Model from '../../model/Model';
 import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle';
@@ -43,7 +42,6 @@ import { GeoSVGResource } from '../../coord/geo/GeoSVGResource';
 
 
 interface RegionsGroup extends graphic.Group {
-    __regions: Region[]
 }
 
 function getFixedItemStyle(model: Model<GeoItemStyleOption>) {
@@ -152,7 +150,9 @@ class MapDraw {
             && data.getVisual('visualMeta')
             && data.getVisual('visualMeta').length > 0;
 
+
         zrUtil.each(geo.regions, function (region) {
+
             // Consider in GeoJson properties.name may be duplicated, for example,
             // there is multiple region named "United Kindom" or "France" (so many
             // colonies). And it is not appropriate to merge them in geo, which
@@ -286,15 +286,10 @@ class MapDraw {
                 const textEl = new graphic.Text({
                     x: centerPt[0],
                     y: centerPt[1],
-                    // FIXME
-                    // label rotation is not support yet in geo or regions of series-map
-                    // that has no data. The rotation will be effected by this `scale`.
-                    // So needed to change to RectText?
-                    scaleX: 1 / group.scaleX,
-                    scaleY: 1 / group.scaleY,
                     z2: 10,
                     silent: true
                 });
+                textEl.afterUpdate = labelTextAfterUpdate;
 
                 setLabelStyle<typeof query>(
                     textEl, getLabelStatesModels(regionModel),
@@ -316,13 +311,6 @@ class MapDraw {
 
                 (compoundPath as ECElement).disableLabelAnimation = true;
 
-                if (!isFirstDraw) {
-                    // Text animation
-                    graphic.updateProps(textEl, {
-                        scaleX: 1 / transformInfoRoam.scaleX,
-                        scaleY: 1 / transformInfoRoam.scaleY
-                    }, mapOrGeoModel);
-                }
             }
 
             // setItemGraphicEl, setHoverStyle after all polygons and labels
@@ -342,9 +330,6 @@ class MapDraw {
                 };
             }
 
-            const groupRegions = regionGroup.__regions || (regionGroup.__regions = []);
-            groupRegions.push(region);
-
             // @ts-ignore FIXME:TS fix the "compatible with each other"?
             regionGroup.highDownSilentOnTouch = !!mapOrGeoModel.get('selectedMode');
             enableHoverEmphasis(regionGroup, emphasisModel.get('focus'), emphasisModel.get('blurScope'));
@@ -387,8 +372,8 @@ class MapDraw {
         }
         const resource = geoSourceManager.getGeoResource(mapName);
         if (resource && resource.type === 'svg') {
-            const root = (resource as GeoSVGResource).useGraphic(this.uid);
-            this._svgGroup.add(root);
+            const svgGraphic = (resource as GeoSVGResource).useGraphic(this.uid);
+            this._svgGroup.add(svgGraphic.root);
         }
     }
 
@@ -450,15 +435,6 @@ class MapDraw {
                 originY: e.originY
             }));
 
-            const group = this.group;
-            this._regionsGroup.traverse(function (el) {
-                const textContent = el.getTextContent();
-                if (textContent) {
-                    textContent.scaleX = 1 / group.scaleX;
-                    textContent.scaleY = 1 / group.scaleY;
-                    textContent.markRedraw();
-                }
-            });
         }, this);
 
         controller.setPointerChecker(function (e, x, y) {
@@ -495,4 +471,17 @@ class MapDraw {
 
 };
 
+function labelTextAfterUpdate(this: graphic.Text) {
+    // Make the label text do not scale but perform translate
+    // based on its host el.
+    const m = this.transform;
+    const scaleX = Math.sqrt(m[0] * m[0] + m[1] * m[1]);
+    const scaleY = Math.sqrt(m[2] * m[2] + m[3] * m[3]);
+
+    m[0] /= scaleX;
+    m[1] /= scaleX;
+    m[2] /= scaleY;
+    m[3] /= scaleY;
+}
+
 export default MapDraw;
diff --git a/src/coord/geo/GeoSVGResource.ts b/src/coord/geo/GeoSVGResource.ts
index 14a29c3..0a3a4ec 100644
--- a/src/coord/geo/GeoSVGResource.ts
+++ b/src/coord/geo/GeoSVGResource.ts
@@ -24,18 +24,23 @@ import {assert, createHashMap, HashMap} from 'zrender/src/core/util';
 import BoundingRect from 'zrender/src/core/BoundingRect';
 import { GeoResource, GeoSVGSourceInput } from './geoTypes';
 import { parseXML } from 'zrender/src/tool/parseXML';
+import Element from 'zrender/src/Element';
 
+export interface GeoSVGGraphic {
+    root: Group;
+    namedElements: Element[];
+}
 
 export class GeoSVGResource implements GeoResource {
 
     readonly type = 'svg';
     private _mapName: string;
     private _parsedXML: SVGElement;
-    private _rootForRect: Group;
+    private _rootForRect: GeoSVGGraphic;
     private _boundingRect: BoundingRect;
     // key: hostKey, value: root
-    private _usedRootMap: HashMap<Group> = createHashMap();
-    private _freedRoots: Group[] = [];
+    private _usedRootMap: HashMap<GeoSVGGraphic> = createHashMap();
+    private _freedRoots: GeoSVGGraphic[] = [];
 
     constructor(
         mapName: string,
@@ -62,10 +67,10 @@ export class GeoSVGResource implements GeoResource {
 
         const graphic = buildGraphic(this._parsedXML);
 
-        this._rootForRect = graphic.root;
+        this._rootForRect = graphic;
         this._boundingRect = graphic.boundingRect;
 
-        this._freedRoots.push(graphic.root);
+        this._freedRoots.push(graphic);
 
         return { boundingRect: graphic.boundingRect };
     }
@@ -78,26 +83,26 @@ export class GeoSVGResource implements GeoResource {
     //     and it is called without view info.
     // So we maintain graphic elements in this module, and enables `view` to use/return these
     // graphics from/to the pool with it's uid.
-    useGraphic(hostKey: string): Group {
+    useGraphic(hostKey: string): GeoSVGGraphic {
         const usedRootMap = this._usedRootMap;
 
-        let root = usedRootMap.get(hostKey);
-        if (root) {
-            return root;
+        let svgGraphic = usedRootMap.get(hostKey);
+        if (svgGraphic) {
+            return svgGraphic;
         }
 
-        root = this._freedRoots.pop() || buildGraphic(this._parsedXML, this._boundingRect).root;
+        svgGraphic = this._freedRoots.pop() || buildGraphic(this._parsedXML, this._boundingRect);
 
-        return usedRootMap.set(hostKey, root);
+        return usedRootMap.set(hostKey, svgGraphic);
     }
 
     freeGraphic(hostKey: string): void {
         const usedRootMap = this._usedRootMap;
 
-        const root = usedRootMap.get(hostKey);
-        if (root) {
+        const svgGraphic = usedRootMap.get(hostKey);
+        if (svgGraphic) {
             usedRootMap.removeKey(hostKey);
-            this._freedRoots.push(root);
+            this._freedRoots.push(svgGraphic);
         }
     }
 
@@ -110,6 +115,7 @@ function buildGraphic(
 ): {
     root: Group;
     boundingRect: BoundingRect;
+    namedElements: Element[]
 } {
     let result;
     let root;
@@ -161,6 +167,7 @@ function buildGraphic(
 
     return {
         root: root,
-        boundingRect: boundingRect
+        boundingRect: boundingRect,
+        namedElements: result.namedElements
     };
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org


[echarts] 01/06: fix: tweak

Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a commit to branch fix/geo-svg
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit cc7001d801728e545b6880fd2e3d94ba49aaf4e9
Author: 100pah <su...@gmail.com>
AuthorDate: Tue Feb 9 16:52:03 2021 +0800

    fix: tweak
---
 src/coord/View.ts    | 5 +++--
 src/coord/geo/Geo.ts | 3 ++-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/coord/View.ts b/src/coord/View.ts
index f724650..17bafc8 100644
--- a/src/coord/View.ts
+++ b/src/coord/View.ts
@@ -77,14 +77,14 @@ class View extends Transformable implements CoordinateSystemMaster, CoordinateSy
     }
 
     setViewRect(x: number, y: number, width: number, height: number): void {
-        this.transformTo(x, y, width, height);
+        this._transformTo(x, y, width, height);
         this._viewRect = new BoundingRect(x, y, width, height);
     }
 
     /**
      * Transformed to particular position and size
      */
-    transformTo(x: number, y: number, width: number, height: number): void {
+    protected _transformTo(x: number, y: number, width: number, height: number): void {
         const rect = this.getBoundingRect();
         const rawTransform = this._rawTransformable;
 
@@ -92,6 +92,7 @@ class View extends Transformable implements CoordinateSystemMaster, CoordinateSy
             new BoundingRect(x, y, width, height)
         );
 
+        // Hint: only works before `this._updateTransform` firstly called.
         rawTransform.decomposeTransform();
 
         this._updateTransform();
diff --git a/src/coord/geo/Geo.ts b/src/coord/geo/Geo.ts
index 75b92cb..85a3954 100644
--- a/src/coord/geo/Geo.ts
+++ b/src/coord/geo/Geo.ts
@@ -82,7 +82,7 @@ class Geo extends View {
         return false;
     }
 
-    transformTo(x: number, y: number, width: number, height: number): void {
+    protected _transformTo(x: number, y: number, width: number, height: number): void {
         let rect = this.getBoundingRect();
         const invertLongitute = this._invertLongitute;
 
@@ -99,6 +99,7 @@ class Geo extends View {
             new BoundingRect(x, y, width, height)
         );
 
+        // Hint: only works before `this._updateTransform` firstly called.
         rawTransformable.decomposeTransform();
 
         if (invertLongitute) {


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org