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/31 22:36:48 UTC

[echarts] 02/02: featrue: [geo] (1) support name on . (2) some refactor.

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 8235095d6547ca062086bacccdc3963c9e92ed08
Author: 100pah <su...@gmail.com>
AuthorDate: Thu Apr 1 06:35:56 2021 +0800

    featrue: [geo]
    (1) support name on <g>.
    (2) some refactor.
---
 src/component/helper/MapDraw.ts | 497 ++++++++++++++++++++++++----------------
 src/coord/geo/Geo.ts            |   2 +-
 src/coord/geo/GeoSVGResource.ts | 171 ++++++++------
 src/coord/geo/geoTypes.ts       |  18 --
 src/core/echarts.ts             |  40 ++--
 test/geo-svg-demo.html          | 298 +++++++++++++++++++++---
 test/geo-svg.html               | 249 ++++++++++----------
 7 files changed, 818 insertions(+), 457 deletions(-)

diff --git a/src/component/helper/MapDraw.ts b/src/component/helper/MapDraw.ts
index c589119..22caaeb 100644
--- a/src/component/helper/MapDraw.ts
+++ b/src/component/helper/MapDraw.ts
@@ -26,7 +26,8 @@ import {
     enableHoverEmphasis,
     DISPLAY_STATES,
     enableComponentHighDownFeatures,
-    setDefaultStateProxy
+    setDefaultStateProxy,
+    SPECIAL_STATES
 } from '../../util/states';
 import geoSourceManager from '../../coord/geo/geoSourceManager';
 import {getUID} from '../../util/component';
@@ -39,7 +40,7 @@ import GeoView from '../geo/GeoView';
 import MapView from '../../chart/map/MapView';
 import Geo from '../../coord/geo/Geo';
 import Model from '../../model/Model';
-import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle';
+import { setLabelStyle, getLabelStatesModels, createTextConfig } from '../../label/labelStyle';
 import { getECData } from '../../util/innerStore';
 import { createOrUpdatePatternFromDecal } from '../../util/decal';
 import { ViewCoordSysTransformInfoPart } from '../../coord/View';
@@ -48,14 +49,16 @@ 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';
-import { RegionGraphic } from '../../coord/geo/geoTypes';
-import { ItemStyleProps } from '../../model/mixin/itemStyle';
-import { LineStyleProps } from '../../model/mixin/lineStyle';
+import { SVGNodeTagLower } from 'zrender/src/tool/parseSVG';
 
 
 interface RegionsGroup extends graphic.Group {
 }
 
+type RegionModel = ReturnType<GeoModel['getRegionModel']> | ReturnType<MapSeries['getRegionModel']>;
+
+type MapOrGeoModel = GeoModel | MapSeries;
+
 interface ViewBuildContext {
     api: ExtensionAPI;
     geo: Geo;
@@ -70,6 +73,26 @@ interface GeoStyleableOption {
     itemStyle?: GeoItemStyleOption;
     lineStyle?: LineStyleOption;
 }
+type RegionName = string;
+
+/**
+ * Only these tags enable use `itemStyle` if they are named in SVG.
+ * Other tags like <text> <tspan> <image> might not suitable for `itemStyle`.
+ * They will not be considered to be styled until some requirements come.
+ */
+const OPTION_STYLE_ENABLED_TAGS: SVGNodeTagLower[] = [
+    'rect', 'circle', 'line', 'ellipse', 'polygon', 'polyline', 'path'
+];
+const OPTION_STYLE_ENABLED_TAG_MAP = zrUtil.createHashMap<number, SVGNodeTagLower>(
+    OPTION_STYLE_ENABLED_TAGS
+);
+const STATE_TRIGGER_TAG_MAP = zrUtil.createHashMap<number, SVGNodeTagLower>(
+    OPTION_STYLE_ENABLED_TAGS.concat(['g']) as SVGNodeTagLower[]
+);
+const LABEL_HOST_MAP = zrUtil.createHashMap<number, SVGNodeTagLower>(
+    OPTION_STYLE_ENABLED_TAGS.concat(['g']) as SVGNodeTagLower[]
+);
+
 
 function getFixedItemStyle(model: Model<GeoItemStyleOption>) {
     const itemStyle = model.getItemStyle();
@@ -115,6 +138,10 @@ class MapDraw {
 
     private _svgGraphicRecord: GeoSVGGraphicRecord;
 
+    // A name may correspond to multiple graphics.
+    // Used as event dispatcher.
+    private _svgDispatcherMap: zrUtil.HashMap<Element[], RegionName>;
+
 
     constructor(api: ExtensionAPI) {
         const group = new graphic.Group();
@@ -199,6 +226,8 @@ class MapDraw {
         const nameMap = this._regionsGroupByName = zrUtil.createHashMap<RegionsGroup>();
         const regionsGroup = this._regionsGroup;
         const transformInfoRaw = viewBuildCtx.transformInfoRaw;
+        const mapOrGeoModel = viewBuildCtx.mapOrGeoModel;
+        const data = viewBuildCtx.data;
 
         const transformPoint = function (point: number[]): number[] {
             return [
@@ -211,14 +240,31 @@ class MapDraw {
 
         // Only when the resource is GeoJSON, there is `geo.regions`.
         zrUtil.each(viewBuildCtx.geo.regions, function (region: GeoJSONRegion) {
+            const regionName = region.name;
+            const regionModel = mapOrGeoModel.getRegionModel(regionName);
+            const dataIdx = data ? data.indexOfName(regionName) : null;
 
             // 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
             // will make them share the same label and bring trouble in label
             // location calculation.
-            const regionGroup = nameMap.get(region.name)
-                || nameMap.set(region.name, new graphic.Group() as RegionsGroup);
+            let regionGroup = nameMap.get(regionName);
+
+            if (!regionGroup) {
+                regionGroup = nameMap.set(regionName, new graphic.Group() as RegionsGroup);
+                regionsGroup.add(regionGroup);
+
+                resetEventTriggerForRegion(
+                    viewBuildCtx, regionGroup, regionName, regionModel, mapOrGeoModel, dataIdx
+                );
+                resetTooltipForRegion(
+                    viewBuildCtx, regionGroup, regionName, regionModel, mapOrGeoModel
+                );
+                resetStateTriggerForRegion(
+                    viewBuildCtx, regionGroup, regionName, regionModel, mapOrGeoModel
+                );
+            }
 
             const compoundPath = new graphic.CompoundPath({
                 segmentIgnoreThreshold: 1,
@@ -258,18 +304,18 @@ class MapDraw {
                 }
             });
 
+            applyOptionStyleForRegion(
+                viewBuildCtx, compoundPath, dataIdx, regionModel
+            );
+
+            if (compoundPath instanceof Displayable) {
+                compoundPath.culling = true;
+            }
+
             const centerPt = transformPoint(region.getCenter());
-            const regionGraphic: RegionGraphic = {
-                name: region.name,
-                el: compoundPath,
-                optionStyleEnabled: true,
-                stateTrigger: regionGroup,
-                eventTrigger: regionGroup,
-                useLabel: true
-            };
-            this._resetSingleRegionGraphic(viewBuildCtx, regionGraphic, centerPt, null, false);
-
-            regionsGroup.add(regionGroup);
+            resetLabelForRegion(
+                viewBuildCtx, compoundPath, regionName, regionModel, mapOrGeoModel, dataIdx, centerPt
+            );
 
         }, this);
     }
@@ -288,22 +334,69 @@ class MapDraw {
             this._useSVG(mapName);
         }
 
+        const svgDispatcherMap = this._svgDispatcherMap = zrUtil.createHashMap<Element[], RegionName>();
+
         let focusSelf = false;
-        zrUtil.each(this._svgGraphicRecord.regionGraphics, function (regionGraphic) {
+        zrUtil.each(this._svgGraphicRecord.named, function (namedItem) {
             // Note that we also allow different elements have the same name.
             // For example, a glyph of a city and the label of the city have
             // the same name and their tooltip info can be defined in a single
             // region option.
-            const focus = this._resetSingleRegionGraphic(
-                viewBuildCtx, regionGraphic, [0, 0], 'inside',
-                // We do not know how the SVG like so we'd better not to change z2.
-                // Otherwise it might bring some unexpected result. For example,
-                // an area hovered that make some inner city can not be clicked.
-                true
-            );
-            if (focus === 'self') {
-                focusSelf = true;
+
+            const regionName = namedItem.name;
+            const mapOrGeoModel = viewBuildCtx.mapOrGeoModel;
+            const data = viewBuildCtx.data;
+            const svgNodeTagLower = namedItem.svgNodeTagLower;
+            const el = namedItem.el;
+
+            const dataIdx = data ? data.indexOfName(regionName) : null;
+            const regionModel = mapOrGeoModel.getRegionModel(regionName);
+
+            if (OPTION_STYLE_ENABLED_TAG_MAP.get(svgNodeTagLower) != null
+                && (el instanceof Displayable)
+            ) {
+                applyOptionStyleForRegion(viewBuildCtx, el, dataIdx, regionModel);
+            }
+
+            if (el instanceof Displayable) {
+                el.culling = true;
+            }
+
+            // We do not know how the SVG like so we'd better not to change z2.
+            // Otherwise it might bring some unexpected result. For example,
+            // an area hovered that make some inner city can not be clicked.
+            (el as ECElement).z2EmphasisLift = 0;
+
+            // If self named, that is, if tag is inside a named <g> (where `namedFrom` does not exists):
+            if (!namedItem.namedFrom) {
+                // label should batter to be displayed based on the center of <g>
+                // if it is named rather than displayed on each child.
+                if (LABEL_HOST_MAP.get(svgNodeTagLower) != null) {
+                    resetLabelForRegion(
+                        viewBuildCtx, el, regionName, regionModel, mapOrGeoModel, dataIdx, [0, 0]
+                    );
+                }
+
+                resetEventTriggerForRegion(
+                    viewBuildCtx, el, regionName, regionModel, mapOrGeoModel, dataIdx
+                );
+
+                resetTooltipForRegion(
+                    viewBuildCtx, el, regionName, regionModel, mapOrGeoModel
+                );
+
+                if (STATE_TRIGGER_TAG_MAP.get(svgNodeTagLower) != null) {
+                    const focus = resetStateTriggerForRegion(
+                        viewBuildCtx, el, regionName, regionModel, mapOrGeoModel
+                    );
+                    if (focus === 'self') {
+                        focusSelf = true;
+                    }
+                    const els = svgDispatcherMap.get(regionName) || svgDispatcherMap.set(regionName, []);
+                    els.push(el);
+                }
             }
+
         }, this);
 
         // It's a little complicated to support blurring the entire geoSVG in series-map.
@@ -334,157 +427,6 @@ class MapDraw {
         }
     }
 
-    private _resetSingleRegionGraphic(
-        viewBuildCtx: ViewBuildContext,
-        regionGraphic: RegionGraphic,
-        labelXY: number[],
-        labelPosition: ElementTextConfig['position'],
-        noZ2EmphasisLift: boolean
-    ): InnerFocus {
-
-        const regionName = regionGraphic.name;
-        const mapOrGeoModel = viewBuildCtx.mapOrGeoModel;
-        const data = viewBuildCtx.data;
-        const isGeo = viewBuildCtx.isGeo;
-
-        const dataIdx = data ? data.indexOfName(regionName) : null;
-        const regionModel = mapOrGeoModel.getRegionModel(regionName);
-
-        applyOptionStyleForRegion(viewBuildCtx, regionGraphic, dataIdx, regionModel);
-
-        if (regionGraphic.el instanceof Displayable) {
-            regionGraphic.el.culling = true;
-        }
-        if (noZ2EmphasisLift) {
-            (regionGraphic.el as ECElement).z2EmphasisLift = 0;
-        }
-
-        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 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
-        // 3. Region has no series legendSymbol, which will be add a showLabel flag in mapSymbolLayout
-        if (
-            regionGraphic.useLabel
-            && (
-                ((isGeo || isDataNaN) && showLabel)
-                || (itemLayout && itemLayout.showLabel)
-            )
-        ) {
-            const query = !isGeo ? dataIdx : regionName;
-            let labelFetcher;
-
-            // Consider dataIdx not found.
-            if (!data || dataIdx >= 0) {
-                labelFetcher = mapOrGeoModel;
-            }
-
-            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'
-                } }
-            );
-
-            regionGraphic.el.setTextContent(textEl);
-            regionGraphic.el.setTextConfig({
-                local: true,
-                insideFill: textEl.style.fill,
-                position: labelPosition
-            });
-            (regionGraphic.el as ECElement).disableLabelAnimation = true;
-        }
-        else {
-            regionGraphic.el.removeTextContent();
-            regionGraphic.el.removeTextConfig();
-            (regionGraphic.el as ECElement).disableLabelAnimation = null;
-        }
-
-        // setItemGraphicEl, setHoverStyle after all polygons and labels
-        // are added to the rigionGroup
-        if (data) {
-            // FIXME: when series-map use a SVG map, and there are duplicated name specified
-            // on different SVG elements, after `data.setItemGraphicEl(...)`:
-            // (1) all of them will be mounted with `dataIndex`, `seriesIndex`, so that tooltip
-            // can be triggered only mouse hover. That's correct.
-            // (2) only the last element will be kept in `data`, so that if trigger tooltip
-            // by `dispatchAction`, only the last one can be found and triggered. That might be
-            // not correct. We will fix it in future if anyone demanding that.
-            data.setItemGraphicEl(dataIdx, regionGraphic.eventTrigger);
-        }
-        // series-map will not trigger "geoselectchange" no matter it is
-        // based on a declared geo component. Becuause series-map will
-        // trigger "selectchange". If it trigger both the two events,
-        // If users call `chart.dispatchAction({type: 'toggleSelect'})`,
-        // it not easy to also fire event "geoselectchanged".
-        else {
-            // Package custom mouse event for geo component
-            getECData(regionGraphic.eventTrigger).eventData = {
-                componentType: 'geo',
-                componentIndex: mapOrGeoModel.componentIndex,
-                geoIndex: mapOrGeoModel.componentIndex,
-                name: regionName,
-                region: (regionModel && regionModel.option) || {}
-            };
-        }
-
-        if (!data) {
-            graphic.setTooltipConfig({
-                el: regionGraphic.el,
-                componentModel: mapOrGeoModel,
-                itemName: regionName,
-                // @ts-ignore FIXME:TS fix the "compatible with each other"?
-                itemTooltipOption: regionModel.get('tooltip')
-            });
-        }
-
-        let focus;
-        const stateTrigger = regionGraphic.stateTrigger;
-        if (stateTrigger) {
-            // @ts-ignore FIXME:TS fix the "compatible with each other"?
-            stateTrigger.highDownSilentOnTouch = !!mapOrGeoModel.get('selectedMode');
-            // @ts-ignore FIXME:TS fix the "compatible with each other"?
-            const emphasisModel = regionModel.getModel('emphasis');
-            focus = emphasisModel.get('focus');
-            enableHoverEmphasis(
-                stateTrigger, focus, emphasisModel.get('blurScope')
-            );
-            if (isGeo) {
-                enableComponentHighDownFeatures(stateTrigger, mapOrGeoModel as GeoModel, regionName);
-            }
-        }
-
-        return focus;
-    }
-
     remove(): void {
         this._regionsGroup.removeAll();
         this._regionsGroupByName = null;
@@ -509,7 +451,7 @@ class MapDraw {
             }
         }
         else if (geo.resourceType === 'geoSVG') {
-            return this._svgGraphicRecord.regionElementMap.get(name) || [];
+            return this._svgDispatcherMap && this._svgDispatcherMap.get(name) || [];
         }
     }
 
@@ -538,6 +480,7 @@ class MapDraw {
             (resource as GeoSVGResource).freeGraphic(this.uid);
         }
         this._svgGraphicRecord = null;
+        this._svgDispatcherMap = null;
         this._svgGroup.removeAll();
         this._svgMapName = null;
     }
@@ -641,7 +584,7 @@ function labelTextAfterUpdate(this: graphic.Text) {
 
 function applyOptionStyleForRegion(
     viewBuildCtx: ViewBuildContext,
-    regionGraphic: RegionGraphic,
+    el: Displayable,
     dataIndex: number,
     regionModel: Model<
         GeoStyleableOption & {
@@ -651,14 +594,6 @@ function applyOptionStyleForRegion(
         }
     >
 ): void {
-
-    if (
-        !regionGraphic.optionStyleEnabled
-        || !(regionGraphic.el instanceof Displayable)
-    ) {
-        return;
-    }
-
     // All of the path are using `itemStyle`, becuase
     // (1) Some SVG also use fill on polyline (The different between
     // polyline and polygon is "open" or "close" but not fill or not).
@@ -698,11 +633,185 @@ function applyOptionStyleForRegion(
 
     // SVG text, tspan and image can be named but not supporeted
     // to be styled by region option yet.
-    regionGraphic.el.setStyle(normalStyle);
-    regionGraphic.el.style.strokeNoScale = true;
-    regionGraphic.el.ensureState('emphasis').style = emphasisStyle;
-    regionGraphic.el.ensureState('select').style = selectStyle;
-    regionGraphic.el.ensureState('blur').style = blurStyle;
+    el.setStyle(normalStyle);
+    el.style.strokeNoScale = true;
+    el.ensureState('emphasis').style = emphasisStyle;
+    el.ensureState('select').style = selectStyle;
+    el.ensureState('blur').style = blurStyle;
+}
+
+function resetLabelForRegion(
+    viewBuildCtx: ViewBuildContext,
+    el: Element,
+    regionName: string,
+    regionModel: RegionModel,
+    mapOrGeoModel: MapOrGeoModel,
+    // Exist only if `viewBuildCtx.data` exists.
+    dataIdx: number,
+    labelXY: number[]
+): void {
+    const data = viewBuildCtx.data;
+    const isGeo = viewBuildCtx.isGeo;
+
+    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 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
+    // 3. 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;
+        }
+
+        const textEl = new graphic.Text({
+            x: labelXY[0],
+            y: labelXY[1],
+            z2: 10,
+            silent: true
+        });
+        textEl.afterUpdate = labelTextAfterUpdate;
+
+        const labelStateModels = getLabelStatesModels(regionModel);
+        setLabelStyle<typeof query>(
+            textEl,
+            labelStateModels,
+            {
+                labelFetcher: labelFetcher,
+                labelDataIndex: query,
+                defaultText: regionName
+            },
+            { normal: {
+                align: 'center',
+                verticalAlign: 'middle'
+            } }
+        );
+
+        // PENDING: use `setLabelStyle` entirely.
+        el.setTextContent(textEl);
+
+        const textConfig = createTextConfig(labelStateModels.normal, null, false);
+        // Need to apply the `translate`.
+        textConfig.local = true;
+        textConfig.insideFill = textEl.style.fill;
+        el.setTextConfig(textConfig);
+
+        for (let i = 0; i < SPECIAL_STATES.length; i++) {
+            const stateName = SPECIAL_STATES[i];
+            // Hover label only work when `emphasis` state ensured.
+            const state = el.ensureState(stateName);
+
+            const textConfig = state.textConfig = createTextConfig(labelStateModels[stateName], null, true);
+            // Need to apply the `translate`.
+            textConfig.local = true;
+            textConfig.insideFill = textEl.style.fill;
+        }
+
+        (el as ECElement).disableLabelAnimation = true;
+    }
+    else {
+        el.removeTextContent();
+        el.removeTextConfig();
+        (el as ECElement).disableLabelAnimation = null;
+    }
+}
+
+function resetEventTriggerForRegion(
+    viewBuildCtx: ViewBuildContext,
+    eventTrigger: Element,
+    regionName: string,
+    regionModel: RegionModel,
+    mapOrGeoModel: MapOrGeoModel,
+    // Exist only if `viewBuildCtx.data` exists.
+    dataIdx: number
+): void {
+    // setItemGraphicEl, setHoverStyle after all polygons and labels
+    // are added to the rigionGroup
+    if (viewBuildCtx.data) {
+        // FIXME: when series-map use a SVG map, and there are duplicated name specified
+        // on different SVG elements, after `data.setItemGraphicEl(...)`:
+        // (1) all of them will be mounted with `dataIndex`, `seriesIndex`, so that tooltip
+        // can be triggered only mouse hover. That's correct.
+        // (2) only the last element will be kept in `data`, so that if trigger tooltip
+        // by `dispatchAction`, only the last one can be found and triggered. That might be
+        // not correct. We will fix it in future if anyone demanding that.
+        viewBuildCtx.data.setItemGraphicEl(dataIdx, eventTrigger);
+    }
+    // series-map will not trigger "geoselectchange" no matter it is
+    // based on a declared geo component. Becuause series-map will
+    // trigger "selectchange". If it trigger both the two events,
+    // If users call `chart.dispatchAction({type: 'toggleSelect'})`,
+    // it not easy to also fire event "geoselectchanged".
+    else {
+        // Package custom mouse event for geo component
+        getECData(eventTrigger).eventData = {
+            componentType: 'geo',
+            componentIndex: mapOrGeoModel.componentIndex,
+            geoIndex: mapOrGeoModel.componentIndex,
+            name: regionName,
+            region: (regionModel && regionModel.option) || {}
+        };
+    }
+}
+
+function resetTooltipForRegion(
+    viewBuildCtx: ViewBuildContext,
+    el: Element,
+    regionName: string,
+    regionModel: RegionModel,
+    mapOrGeoModel: MapOrGeoModel
+): void {
+    if (!viewBuildCtx.data) {
+        graphic.setTooltipConfig({
+            el: el,
+            componentModel: mapOrGeoModel,
+            itemName: regionName,
+            // @ts-ignore FIXME:TS fix the "compatible with each other"?
+            itemTooltipOption: regionModel.get('tooltip')
+        });
+    }
+}
+
+function resetStateTriggerForRegion(
+    viewBuildCtx: ViewBuildContext,
+    el: Element,
+    regionName: string,
+    regionModel: RegionModel,
+    mapOrGeoModel: MapOrGeoModel
+): InnerFocus {
+    // @ts-ignore FIXME:TS fix the "compatible with each other"?
+    el.highDownSilentOnTouch = !!mapOrGeoModel.get('selectedMode');
+    // @ts-ignore FIXME:TS fix the "compatible with each other"?
+    const emphasisModel = regionModel.getModel('emphasis');
+    const focus = emphasisModel.get('focus');
+    enableHoverEmphasis(
+        el, focus, emphasisModel.get('blurScope')
+    );
+    if (viewBuildCtx.isGeo) {
+        enableComponentHighDownFeatures(el, mapOrGeoModel as GeoModel, regionName);
+    }
+
+    return focus;
 }
 
 export default MapDraw;
diff --git a/src/coord/geo/Geo.ts b/src/coord/geo/Geo.ts
index 430bdf4..a2e96cc 100644
--- a/src/coord/geo/Geo.ts
+++ b/src/coord/geo/Geo.ts
@@ -169,7 +169,7 @@ class Geo extends View {
         return this._nameCoordMap.get(name) || (region && region.getCenter());
     }
 
-    dataToPoint(data: number[], noRoam?: boolean, out?: number[]): number[] {
+    dataToPoint(data: number[] | string, noRoam?: boolean, out?: number[]): number[] {
         if (typeof data === 'string') {
             // Map area name to geoCoord
             data = this.getGeoCoord(data);
diff --git a/src/coord/geo/GeoSVGResource.ts b/src/coord/geo/GeoSVGResource.ts
index 79455c3..93d9c2f 100644
--- a/src/coord/geo/GeoSVGResource.ts
+++ b/src/coord/geo/GeoSVGResource.ts
@@ -17,38 +17,54 @@
 * under the License.
 */
 
-import { parseSVG, makeViewBoxTransform, SVGNodeTagLower } from 'zrender/src/tool/parseSVG';
+import { parseSVG, makeViewBoxTransform, SVGNodeTagLower, SVGParserResultNamedItem } from 'zrender/src/tool/parseSVG';
 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 {assert, createHashMap, each, HashMap} from 'zrender/src/core/util';
 import BoundingRect from 'zrender/src/core/BoundingRect';
-import { GeoResource, GeoSVGGraphicRoot, GeoSVGSourceInput, RegionGraphic } from './geoTypes';
+import { GeoResource, GeoSVGGraphicRoot, GeoSVGSourceInput } from './geoTypes';
 import { parseXML } from 'zrender/src/tool/parseXML';
 import { GeoSVGRegion } from './Region';
 import Element from 'zrender/src/Element';
 
-type RegionName = string;
 export interface GeoSVGGraphicRecord {
     root: Group;
     boundingRect: BoundingRect;
-    regionGraphics: RegionGraphic[];
-    // A name may correspond to multiple graphics.
-    regionElementMap: HashMap<Element[], RegionName>;
+    named: SVGParserResultNamedItem[];
 }
 
+/**
+ * "region available" means that: enable users to set attribute `name="xxx"` on those tags
+ * to make it be a region.
+ * 1. region styles and its label styles can be defined in echarts opton:
+ * ```js
+ * geo: {
+ *     regions: [{
+ *         name: 'xxx',
+ *         itemStyle: { ... },
+ *         label: { ... }
+ *     }, {
+ *         ...
+ *     },
+ *     ...]
+ * };
+ * ```
+ * 2. name can be duplicated in different SVG tag. All of the tags with the same name share
+ * a region option. For exampel if there are two <path> representing two lung lobes. They have
+ * no common parents but both of them need to display label "lung" inside.
+ */
 const REGION_AVAILABLE_SVG_TAG_MAP = createHashMap<number, SVGNodeTagLower>([
-    'rect', 'circle', 'line', 'ellipse', 'polygon', 'polyline', 'text', 'tspan', 'path'
+    'rect', 'circle', 'line', 'ellipse', 'polygon', 'polyline', 'path',
+    // <text> <tspan> are also enabled becuase some SVG might paint text itself,
+    // but still need to trigger events or tooltip.
+    'text', 'tspan',
+    // <g> is also enabled because this case: if multiple tags share one name
+    // and need label displayed, every tags will display the name, which is not
+    // expected. So we can put them into a <g name="xxx">. Thereby only one label
+    // displayed and located based on the bounding rect of the <g>.
+    'g'
 ]);
 
-const OPTION_STYLE_ENABLED_TAG_MAP = createHashMap<number, SVGNodeTagLower>([
-    'rect', 'circle', 'line', 'ellipse', 'polygon', 'polyline', 'path'
-]);
-
-const LABEL_HOST_MAP = createHashMap<number, SVGNodeTagLower>([
-    'rect', 'circle', 'line', 'ellipse', 'polygon', 'polyline', 'path'
-]);
-
-
 export class GeoSVGResource implements GeoResource {
 
     readonly type = 'geoSVG';
@@ -57,9 +73,9 @@ export class GeoSVGResource implements GeoResource {
 
     private _firstGraphic: GeoSVGGraphicRecord;
     private _boundingRect: BoundingRect;
-    private _regions: GeoSVGRegion[] = [];
+    private _regions: GeoSVGRegion[];
     // Key: region.name
-    private _regionsMap: HashMap<GeoSVGRegion> = createHashMap<GeoSVGRegion>();
+    private _regionsMap: HashMap<GeoSVGRegion>;
 
     // All used graphics. key: hostKey, value: root
     private _usedGraphicMap: HashMap<GeoSVGGraphicRecord> = createHashMap();
@@ -98,21 +114,14 @@ export class GeoSVGResource implements GeoResource {
 
             this._boundingRect = this._firstGraphic.boundingRect.clone();
 
-            const regionGraphics = firstGraphic.regionGraphics;
             // PENDING: `nameMap` will not be supported until some real requirement come.
             // if (nameMap) {
-            //     regionGraphics = applyNameMap(regionGraphics, nameMap);
+            //     named = applyNameMap(named, nameMap);
             // }
 
-            // Create resions only for the first graphic.
-            for (let i = 0; i < regionGraphics.length; i++) {
-                const regionGraphic = regionGraphics[i];
-                const region = new GeoSVGRegion(regionGraphic.name, regionGraphic.el);
-                // PENDING: if `nameMap` supported, this region can not be mounted on
-                // `this`, but can only be created each time `load()` called.
-                this._regions.push(region);
-                this._regionsMap.set(regionGraphic.name, region);
-            }
+            const { regions, regionsMap } = createRegions(firstGraphic.named);
+            this._regions = regions;
+            this._regionsMap = regionsMap;
         }
 
         return {
@@ -148,10 +157,10 @@ export class GeoSVGResource implements GeoResource {
 
         // PENDING: `nameMap` will not be supported until some real requirement come.
         // `nameMap` can only be obtained from echarts option.
-        // The original `regionGraphics` must not be modified.
+        // The original `named` must not be modified.
         // if (nameMap) {
         //     svgGraphic = extend({}, svgGraphic);
-        //     svgGraphic.regionGraphics = applyNameMap(svgGraphic.regionGraphics, nameMap);
+        //     svgGraphic.named = applyNameMap(svgGraphic.named, nameMap);
         // }
 
         return svgGraphic;
@@ -227,59 +236,75 @@ function buildGraphic(
 
     (root as GeoSVGGraphicRoot).isGeoSVGGraphicRoot = true;
 
-    const regionGraphics = [] as GeoSVGGraphicRecord['regionGraphics'];
-    const regionElementMap = createHashMap<Element[], RegionName>();
-    const named = result.named;
-    for (let i = 0; i < named.length; i++) {
-        const namedItem = named[i];
-        const svgNodeTagLower = namedItem.svgNodeTagLower;
-
-        if (REGION_AVAILABLE_SVG_TAG_MAP.get(svgNodeTagLower) != null) {
-            const optionStyleEnabled = OPTION_STYLE_ENABLED_TAG_MAP.get(svgNodeTagLower);
-            const el = namedItem.el;
-            const name = namedItem.name;
-
-            regionGraphics.push({
-                name: name,
-                el: el,
-                optionStyleEnabled: optionStyleEnabled != null,
-                stateTrigger: optionStyleEnabled != null ? el : null,
-                // text/tspan/image do not suport style but support event.
-                eventTrigger: el,
-                useLabel: LABEL_HOST_MAP.get(svgNodeTagLower) != null
-            });
-
-            const els = regionElementMap.get(name) || regionElementMap.set(name, []);
-            els.push(el);
-
-            // Only named element has silent: false, other elements should
-            // act as background and has no user interaction.
-            el.silent = false;
-            // text|tspan will be converted to group.
-            if (el.isGroup) {
-                el.traverse(child => {
-                    child.silent = false;
-                });
-            }
+    const named = [] as GeoSVGGraphicRecord['named'];
+
+    each(result.named, namedItem => {
+        if (REGION_AVAILABLE_SVG_TAG_MAP.get(namedItem.svgNodeTagLower) != null) {
+            named.push(namedItem);
+            setSilent(namedItem.el);
         }
+    });
+
+    return { root, boundingRect, named };
+}
+
+function setSilent(el: Element): void {
+    // Only named element has silent: false, other elements should
+    // act as background and has no user interaction.
+    el.silent = false;
+    // text|tspan will be converted to group.
+    if (el.isGroup) {
+        el.traverse(child => {
+            child.silent = false;
+        });
     }
+}
+
+function createRegions(
+    named: SVGParserResultNamedItem[]
+): {
+    regions: GeoSVGRegion[];
+    regionsMap: HashMap<GeoSVGRegion>;
+} {
+
+    const regions: GeoSVGRegion[] = [];
+    const regionsMap = createHashMap<GeoSVGRegion>();
+
+    // Create resions only for the first graphic.
+    each(named, namedItem => {
+        // Region has feature to calculate center for tooltip or other features.
+        // If there is a <g name="xxx">, the center should be the center of the
+        // bounding rect of the g.
+        if (namedItem.namedFrom != null) {
+            return;
+        }
 
-    return { root, boundingRect, regionGraphics, regionElementMap: regionElementMap };
+        const region = new GeoSVGRegion(namedItem.name, namedItem.el);
+        // PENDING: if `nameMap` supported, this region can not be mounted on
+        // `this`, but can only be created each time `load()` called.
+        regions.push(region);
+        // PENDING: if multiple tag named with the same name, only one will be
+        // found by `_regionsMap`. `_regionsMap` is used to find a coordinate
+        // by name. We use `region.getCenter()` as the coordinate.
+        regionsMap.set(namedItem.name, region);
+    });
+
+    return { regions, regionsMap };
 }
 
 
 // PENDING: `nameMap` will not be supported until some real requirement come.
 // /**
 //  * Use the alias in geoNameMap.
-//  * The input `regionGraphics` must not be modified.
+//  * The input `named` must not be modified.
 //  */
 // function applyNameMap(
-//     regionGraphics: GeoSVGGraphicRecord['regionGraphics'],
+//     named: GeoSVGGraphicRecord['named'],
 //     nameMap: NameMap
-// ): GeoSVGGraphicRecord['regionGraphics'] {
-//     const result = [] as GeoSVGGraphicRecord['regionGraphics'];
-//     for (let i = 0; i < regionGraphics.length; i++) {
-//         let regionGraphic = regionGraphics[i];
+// ): GeoSVGGraphicRecord['named'] {
+//     const result = [] as GeoSVGGraphicRecord['named'];
+//     for (let i = 0; i < named.length; i++) {
+//         let regionGraphic = named[i];
 //         const name = regionGraphic.name;
 //         if (nameMap && nameMap.hasOwnProperty(name)) {
 //             regionGraphic = extend({}, regionGraphic);
diff --git a/src/coord/geo/geoTypes.ts b/src/coord/geo/geoTypes.ts
index 8674218..e98de49 100644
--- a/src/coord/geo/geoTypes.ts
+++ b/src/coord/geo/geoTypes.ts
@@ -140,21 +140,3 @@ export interface GeoResource {
 export interface GeoSVGGraphicRoot extends Group {
     isGeoSVGGraphicRoot: boolean;
 }
-
-export type RegionGraphic = {
-    // Region name. Can not be null/undefined.
-    name: string;
-    // Main el.
-    el: Element;
-    // If it specified, use it to trigger state
-    // style change (emphasis/select/blur)
-    // Can be null/undefined.
-    stateTrigger: Element;
-    // If it specified, use it to trigger event to users
-    // Can be null/undefined.
-    eventTrigger: Element;
-    // Whether to set label on `el.textContent`.
-    useLabel: boolean;
-    // Whether to be enabled to set style via in echarts option.
-    optionStyleEnabled: boolean;
-};
diff --git a/src/core/echarts.ts b/src/core/echarts.ts
index 2f652a3..10fc41b 100644
--- a/src/core/echarts.ts
+++ b/src/core/echarts.ts
@@ -36,8 +36,6 @@ import ChartView, {ChartViewConstructor} from '../view/Chart';
 import * as graphic from '../util/graphic';
 import {getECData} from '../util/innerStore';
 import {
-    enterEmphasisWhenMouseOver,
-    leaveEmphasisWhenMouseOut,
     isHighDownDispatcher,
     HOVER_STATE_EMPHASIS,
     HOVER_STATE_BLUR,
@@ -1484,24 +1482,28 @@ class ECharts extends Eventful<ECEventDefinition> {
             ecModel && ecModel.eachComponent(condition, function (model) {
                 if (!excludeSeriesIdMap || excludeSeriesIdMap.get(model.id) == null) {
                     if (isHighDownPayload(payload)) {
-                        if (payload.type === HIGHLIGHT_ACTION_TYPE) {
-                            if (model instanceof SeriesModel) {
-                                !payload.notBlur && blurSeriesFromHighlightPayload(model, payload, ecIns._api);
+                        if (model instanceof SeriesModel) {
+                            if (payload.type === HIGHLIGHT_ACTION_TYPE && !payload.notBlur) {
+                                blurSeriesFromHighlightPayload(model, payload, ecIns._api);
+                            }
+                        }
+                        else {
+                            const { focusSelf, dispatchers } = findComponentHighDownDispatchers(
+                                model.mainType, model.componentIndex, payload.name, ecIns._api
+                            );
+                            if (payload.type === HIGHLIGHT_ACTION_TYPE && focusSelf && !payload.notBlur) {
+                                blurComponent(model.mainType, model.componentIndex, ecIns._api);
                             }
-                            else {
-                                const { focusSelf, dispatchers } = findComponentHighDownDispatchers(
-                                    model.mainType, model.componentIndex, payload.name, ecIns._api
-                                );
-                                if (focusSelf && !payload.notBlur) {
-                                    blurComponent(model.mainType, model.componentIndex, ecIns._api);
-                                }
-                                // PENDING:
-                                // Whether to put this "enter emphasis" code in `ComponentView`,
-                                // which will be the same as `ChartView` but might be not necessary
-                                // and will be far from this logic.
-                                if (dispatchers) {
-                                    each(dispatchers, dispatcher => enterEmphasis(dispatcher));
-                                }
+                            // PENDING:
+                            // Whether to put this "enter emphasis" code in `ComponentView`,
+                            // which will be the same as `ChartView` but might be not necessary
+                            // and will be far from this logic.
+                            if (dispatchers) {
+                                each(dispatchers, dispatcher => {
+                                    payload.type === HIGHLIGHT_ACTION_TYPE
+                                        ? enterEmphasis(dispatcher)
+                                        : leaveEmphasis(dispatcher);
+                                });
                             }
                         }
                     }
diff --git a/test/geo-svg-demo.html b/test/geo-svg-demo.html
index ed5afe8..85ad1b0 100644
--- a/test/geo-svg-demo.html
+++ b/test/geo-svg-demo.html
@@ -38,8 +38,8 @@ under the License.
 
 
         <div id="main_geo_svg_organ"></div>
-        <!-- <div id="main_geo_svg_organ1"></div> -->
-
+        <div id="main_geo_svg_regions"></div>
+        <div id="main_geo_svg_line_path"></div>
 
 
 
@@ -99,6 +99,8 @@ under the License.
                     tooltip: {
                     },
                     geo: {
+                        left: 10,
+                        right: '50%',
                         map: 'seatmap',
                         roam: true,
                         selectedMode: 'multiple',
@@ -107,18 +109,16 @@ under the License.
                         emphasis: {
                             focus: 'self',
                             itemStyle: {
-                                // color: null
+                                color: null
                             },
                             label: {
-                                show: false,
+                                position: 'bottom',
+                                distance: 20,
                                 textBorderColor: '#fff',
                                 textBorderWidth: 2
                             }
                         },
                         blur: {
-                            // itemStyle: {
-                            //     opacity: 0.3
-                            // }
                         },
                         select: {
                             itemStyle: {
@@ -130,7 +130,25 @@ under the License.
                                 textBorderWidth: 2
                             }
                         }
-                    }
+                    },
+                    grid: {
+                        left: '60%'
+                    },
+                    xAxis: {
+                        splitLine: {
+                            show: false
+                        }
+                    },
+                    yAxis: {
+                        data: ['heart', 'large-intestine', 'small-intestine', 'spleen', 'kidney', 'lung', 'liver']
+                    },
+                    series: [{
+                        type: 'bar',
+                        emphasis: {
+                            focus: 'self'
+                        },
+                        data: [121, 321, 141, 52, 198, 289, 139]
+                    }]
                 };
 
                 var chart = testHelper.create(echarts, 'main_geo_svg_organ', {
@@ -149,9 +167,34 @@ under the License.
                 listenAndPrintEvent(chart);
 
                 if (chart) {
-                    chart.on('highlight', function () {
-                        console.log('agsd');
+                    chart.on('mouseover', { seriesIndex: 0 }, function (event) {
+                        chart.dispatchAction({
+                            type: 'highlight',
+                            geoIndex: 0,
+                            name: event.name
+                        });
+                    });
+                    chart.on('mouseout', { seriesIndex: 0 }, function (event) {
+                        chart.dispatchAction({
+                            type: 'downplay',
+                            geoIndex: 0,
+                            name: event.name
+                        });
                     });
+                    // chart.on('mouseover', { geoIndex: 0 }, function (event) {
+                    //     chart.dispatchAction({
+                    //         type: 'highlight',
+                    //         seriesIndex: 0,
+                    //         name: event.name
+                    //     });
+                    // });
+                    // chart.on('mouseout', { geoIndex: 0 }, function (event) {
+                    //     chart.dispatchAction({
+                    //         type: 'downplay',
+                    //         seriesIndex: 0,
+                    //         name: event.name
+                    //     });
+                    // });
                 }
 
             });
@@ -162,36 +205,54 @@ under the License.
 
 
 
-<!--
+
 
 
         <script>
         require(['echarts'/*, 'map/js/china' */], function (echarts) {
             var option;
+
             $.ajax({
-                url: '../../vis-data/map/svg/organ/Veins_Medical_Diagram_clip_art.svg',
+                url: '../../vis-data/map/svg/geo-topo/Sicily_prehellenic_topographic_map.svg',
                 dataType: 'text'
             }).done(function (svg) {
 
-                echarts.registerMap('seatmap', {
+                echarts.registerMap('sicily', {
                     svg: svg
                 });
 
                 option = {
                     tooltip: {
                     },
-                    series: {
-                        type: 'map',
-                        map: 'seatmap',
+                    geo: [{
+                        map: 'sicily',
                         roam: true,
                         selectedMode: 'multiple',
-                        // height: 100,
-                        // zoom: 1.5
+                        itemStyle: {
+                            color: null
+                        },
+                        tooltip: {
+                            show: true,
+                            confine: true,
+                            formatter: function (params) {
+                                return [
+                                    'This is the introduction:',
+                                    'xxxxxxxxxxxxxxxxxxxxx',
+                                    'xxxxxxxxxxxxxxxxxxxxx',
+                                    'xxxxxxxxxxxxxxxxxxxxx',
+                                    'xxxxxxxxxxxxxxxxxxxxx',
+                                    'xxxxxxxxxxxxxxxxxxxxx',
+                                    'xxxxxxxxxxxxxxxxxxxxx',
+                                    'xxxxxxxxxxxxxxxxxxxxx',
+                                    'xxxxxxxxxxxxxxxxxxxxx',
+                                    'xxxxxxxxxxxxxxxxxxxxx',
+                                    'xxxxxxxxxxxxxxxxxxxxx'
+                                ].join('<br>');
+                            }
+                        },
                         emphasis: {
-                            focus: 'self',
                             label: {
-                                textBorderColor: '#fff',
-                                textBorderWidth: 2
+                                show: false
                             }
                         },
                         select: {
@@ -199,39 +260,202 @@ under the License.
                                 color: '#b50205'
                             },
                             label: {
-                                show: false,
-                                textBorderColor: '#fff',
-                                textBorderWidth: 2
+                                show: false
                             }
-                        }
-                    }
+                        },
+                        regions: [{
+                            name: 'Sikeloi',
+                            tooltip: {
+                                formatter: 'Sikeloi',
+                                textStyle: { color: '#555' },
+                                backgroundColor: '#ccc'
+                            }
+                        }, {
+                            name: 'Sikanoi',
+                            tooltip: {
+                                formatter: 'Sikanoi',
+                                textStyle: { color: '#555' },
+                                backgroundColor: '#ccc'
+                            }
+                        }, {
+                            name: 'Elymoi',
+                            tooltip: {
+                                formatter: 'Elymoi',
+                                textStyle: { color: '#555' },
+                                backgroundColor: '#ccc'
+                            }
+                        }],
+                        z: 0
+                    }]
                 };
 
-                var chart = testHelper.create(echarts, 'main_geo_svg_organ1', {
+                var chart = testHelper.create(echarts, 'main_geo_svg_regions', {
                     title: [
-                        'pure geo component with svg resource',
-                        'click seat: check **allSelected** correct.'
+                        'symbol and label use the same name in SVG.',
+                        'Hover each symbol and text, tooltip should be displayed.',
+                        'Hover the three area, tooltip should be displayed.',
+                        'Click, check **selected**.'
                     ],
                     option: option,
                     info: {},
                     infoKey: 'event',
                     height: 500
-                    // buttons: [{text: 'btn-txt', onclick: function () {}}],
-                    // recordCanvas: true,
                 });
 
                 listenAndPrintEvent(chart);
 
-                if (chart) {
-                    chart.on('highlight', function () {
-                        console.log('agsd');
-                    });
-                }
+            });
+
+        });
+        </script>
+
+
+
+
+
+
+
+
+
+
+        <script>
+        require(['echarts'/*, 'map/js/china' */], function (echarts) {
+            var option;
+
+            $.ajax({
+                url: '../../vis-data/map/svg/geo-topo/Map_of_Iceland.svg',
+                dataType: 'text'
+            }).done(function (svg) {
+
+                echarts.registerMap('Map_of_Iceland', {
+                    svg: svg
+                });
+
+                option = {
+                    tooltip: {
+                        alwaysShowContent: true,
+                        enterable: true,
+                        extraCssText: 'user-select: text'
+                    },
+                    geo: [{
+                        map: 'Map_of_Iceland',
+                        roam: true,
+                        selectedMode: 'single',
+                        tooltip: {
+                            show: true,
+                            // confine: true
+                        },
+                        label: {
+                            fontSize: 20,
+                            textBorderColor: '#fff',
+                            textBorderWidth: 2
+                        },
+                        emphasis: {
+                            itemStyle: {
+                                color: null,
+                                borderColor: '#b50805',
+                                borderWidth: 5
+                            }
+                        },
+                        select: {
+                            itemStyle: {
+                                color: null,
+                                borderColor: '#b50205',
+                                borderWidth: 5
+                            }
+                        },
+                        regions: [{
+                            name: 'trip1',
+                            label: {
+                                formatter: 'Western Trip'
+                            },
+                            tooltip: {
+                                position: 'right',
+                                formatter: [
+                                    'Western Trip:',
+                                    'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
+                                    'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
+                                    'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
+                                    'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
+                                    'xxxxxxxxxxxxxxxxxxxxxxxxxxx'
+                                ].join('<br>')
+                            }
+                        }, {
+                            name: 'trip2',
+                            label: {
+                                formatter: 'Eastern Trip'
+                            },
+                            tooltip: {
+                                position: 'left',
+                                formatter: [
+                                    'Western Trip:',
+                                    'xxxxxxxxxxxx',
+                                    'xxxxxxxxxxxx',
+                                    'xxxxxxxxxxxx',
+                                    'xxxxxxxxxxxx',
+                                    'xxxxxxxxxxxx',
+                                    'xxxxxxxxxxxx',
+                                    'xxxxxxxxxxxx',
+                                    'xxxxxxxxxxxx',
+                                    'xxxxxxxxxxxx',
+                                    'xxxxxxxxxxxx',
+                                    'xxxxxxxxxxxx'
+                                ].join('<br>')
+                            }
+                        }]
+                    }]
+                };
+
+                var chart = testHelper.create(echarts, 'main_geo_svg_line_path', {
+                    title: [
+                        'Select some route in SVG via API.',
+                        'Some route should be highlighted (check selectedMode **single**).',
+                        'label should be displayed.'
+                    ],
+                    option: option,
+                    info: {},
+                    infoKey: 'event',
+                    height: 500,
+                    buttons: [{
+                        text: 'highlight trip1',
+                        onclick: function () {
+                            chart.dispatchAction({
+                                type: 'geoSelect',
+                                geoIndex: 0,
+                                name: 'trip1'
+                            });
+                            chart.dispatchAction({
+                                type: 'showTip',
+                                geoIndex: 0,
+                                name: 'trip1'
+                            });
+                        }
+                    }, {
+                        text: 'highlight trip2',
+                        onclick: function () {
+                            chart.dispatchAction({
+                                type: 'geoSelect',
+                                geoIndex: 0,
+                                name: 'trip2'
+                            });
+                            chart.dispatchAction({
+                                type: 'showTip',
+                                geoIndex: 0,
+                                name: 'trip2'
+                            });
+                        }
+                    }]
+                });
+
+                listenAndPrintEvent(chart);
 
             });
 
         });
-        </script> -->
+        </script>
+
+
+
 
 
 
diff --git a/test/geo-svg.html b/test/geo-svg.html
index 2d52f33..45ec6e4 100644
--- a/test/geo-svg.html
+++ b/test/geo-svg.html
@@ -39,10 +39,10 @@ under the License.
 
         <div id="main_simple_geo_json"></div>
         <div id="main_simple_geo_svg"></div>
+        <div id="main_geo_svg_emphasis_select"></div>
         <div id="main_pure_geo_svg"></div>
         <div id="main_pure_map_svg"></div>
         <div id="main_map_geo_svg"></div>
-        <div id="main_geo_svg_regions"></div>
 
 
 
@@ -135,7 +135,7 @@ under the License.
                 option: option,
                 info: {},
                 infoKey: 'event',
-                height: 300
+                height: 200
             });
 
             listenAndPrintEvent(chart);
@@ -187,7 +187,7 @@ under the License.
                 option: option,
                 info: {},
                 infoKey: 'event',
-                height: 300
+                height: 200
             });
 
             listenAndPrintEvent(chart);
@@ -199,6 +199,129 @@ under the License.
 
 
 
+        <script>
+        require(['echarts'/*, 'map/js/china' */], function (echarts) {
+            const svg = [
+                '<?xml version="1.0" encoding="utf-8"?>',
+                '<svg xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" fill-rule="evenodd" xml:space="preserve">',
+                '<path name="a" d="M 0,0 L 0,100 100,100 100,0 Z" fill="rgb(16,193,138)" stroke="green" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>',
+                '<path name="a" d="M 110,0 L 110,100 210,100 210,0 Z" fill="rgb(16,193,138)" stroke="green" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>',
+                '<g name="b">',
+                    '<path d="M 0,110 L 0,210 100,110 Z" fill="rgb(16,193,138)" stroke="green" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>',
+                    '<path d="M 110,110 L 110,210 210,110 Z" fill="rgb(16,193,138)" stroke="green" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>',
+                '</g>',
+                '<radialGradient id="XMLID_1_" cx="0" cy="220" r="59.4363" gradientUnits="userSpaceOnUse">',
+                    '<stop  offset="0" style="stop-color:#E6E6E6"/>',
+                    '<stop  offset="1" style="stop-color:#4D4D4D"/>',
+                '</radialGradient>',
+                '<path name="c" d="M 0,220 L 0,320 100,220 Z" fill="url(#XMLID_1_)" stroke="green" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>',
+                '<radialGradient id="XMLID_2_" cx="110" cy="220" r="59.4363" gradientUnits="userSpaceOnUse">',
+                    '<stop  offset="0" style="stop-color:#E6E6E6"/>',
+                    '<stop  offset="1" style="stop-color:#4D4D4D"/>',
+                '</radialGradient>',
+                '<path name="c" d="M 110,220 L 110,320 210,220 Z" fill="url(#XMLID_2_)" stroke="green" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"/>',
+                '</svg>'
+            ].join('')
+
+            echarts.registerMap('testGeoSVG_select_hover', { svg: svg });
+
+            option = {
+                // tooltip: {
+                // },
+                geo: {
+                    map: 'testGeoSVG_select_hover',
+                    roam: true,
+                    selectedMode: 'single',
+                    // height: '100%',
+                    // center
+                    // layoutCenter: ['30%', 40],
+                    // layoutSize: 40,
+                    // boundingCoords
+                    zoom: 1,
+                    aspectScale: 1,
+                    label: {
+                        show: true
+                    },
+                    emphasis: {
+                        label: {
+                            show: true,
+                            fontSize: 100
+                        }
+                    },
+                    select: {
+                        itemStyle: {
+                            color: 'red'
+                        }
+                    }
+                }
+            };
+
+            var chart = testHelper.create(echarts, 'main_geo_svg_emphasis_select', {
+                title: [
+                    'click buttons',
+                    'hover check'
+                ],
+                option: option,
+                info: {},
+                infoKey: 'event',
+                height: 200,
+                button: [{
+                    text: 'highlight a',
+                    onclick: function () {
+                        chart.dispatchAction({ type: 'highlight', geoIndex: 0, name: 'a' });
+                    }
+                }, {
+                    text: 'downplay a',
+                    onclick: function () {
+                        chart.dispatchAction({ type: 'downplay', geoIndex: 0, name: 'a' });
+                    }
+                }, {
+                    text: 'select a',
+                    onclick: function () {
+                        chart.dispatchAction({ type: 'geoSelect', geoIndex: 0, name: 'a' });
+                    }
+                }, {
+                    text: 'unselect a',
+                    onclick: function () {
+                        chart.dispatchAction({ type: 'geoUnSelect', geoIndex: 0, name: 'a' });
+                    }
+                }, {
+                    text: 'highlight b',
+                    onclick: function () {
+                        chart.dispatchAction({ type: 'highlight', geoIndex: 0, name: 'b' });
+                    }
+                }, {
+                    text: 'downplay b',
+                    onclick: function () {
+                        chart.dispatchAction({ type: 'downplay', geoIndex: 0, name: 'b' });
+                    }
+                }, {
+                    text: 'select b',
+                    onclick: function () {
+                        chart.dispatchAction({ type: 'geoSelect', geoIndex: 0, name: 'b' });
+                    }
+                }, {
+                    text: 'unselect b',
+                    onclick: function () {
+                        chart.dispatchAction({ type: 'geoUnSelect', geoIndex: 0, name: 'b' });
+                    }
+                }]
+            });
+
+            listenAndPrintEvent(chart);
+
+            if (chart) {
+                chart.on('highlight', function () {
+                });
+            }
+
+        });
+        </script>
+
+
+
+
+
 
 
         <script>
@@ -220,6 +343,9 @@ under the License.
                         map: 'seatmap',
                         roam: true,
                         selectedMode: 'multiple',
+                        tooltip: {
+                            show: true
+                        },
                         // height: 100,
                         // zoom: 1.5
                         emphasis: {
@@ -251,7 +377,8 @@ under the License.
                 var chart = testHelper.create(echarts, 'main_pure_geo_svg', {
                     title: [
                         'pure geo component with svg resource',
-                        'click seat: check **allSelected** correct.'
+                        'click seat: check **allSelected** correct.',
+                        'hover seat: check **tooltip** and **label** correct.'
                     ],
                     option: option,
                     info: {},
@@ -359,6 +486,9 @@ under the License.
                         selectedMode: 'multiple',
                         // height: 100,
                         // zoom: 1.5
+                        tooltip: {
+                            show: true
+                        },
                         emphasis: {
                             label: {
                                 textBorderColor: '#fff',
@@ -407,118 +537,7 @@ under the License.
 
 
 
-        <script>
-        require(['echarts'/*, 'map/js/china' */], function (echarts) {
-            var option;
 
-            $.ajax({
-                url: '../../vis-data/map/svg/geo-topo/Sicily_prehellenic_topographic_map.svg',
-                dataType: 'text'
-            }).done(function (svg) {
-
-                echarts.registerMap('sicily', {
-                    svg: svg
-                });
-
-                option = {
-                    tooltip: {
-                    },
-                    geo: [{
-                        map: 'sicily',
-                        roam: true,
-                        selectedMode: 'multiple',
-                        itemStyle: {
-                            color: null
-                        },
-                        tooltip: {
-                            show: true,
-                            confine: true,
-                            formatter: function (params) {
-                                return [
-                                    'This is the introduction:',
-                                    'xxxxxxxxxxxxxxxxxxxxx',
-                                    'xxxxxxxxxxxxxxxxxxxxx',
-                                    'xxxxxxxxxxxxxxxxxxxxx',
-                                    'xxxxxxxxxxxxxxxxxxxxx',
-                                    'xxxxxxxxxxxxxxxxxxxxx',
-                                    'xxxxxxxxxxxxxxxxxxxxx',
-                                    'xxxxxxxxxxxxxxxxxxxxx',
-                                    'xxxxxxxxxxxxxxxxxxxxx',
-                                    'xxxxxxxxxxxxxxxxxxxxx',
-                                    'xxxxxxxxxxxxxxxxxxxxx'
-                                ].join('<br>');
-                            }
-                        },
-                        emphasis: {
-                            label: {
-                                show: false
-                            }
-                        },
-                        select: {
-                            itemStyle: {
-                                color: '#b50205'
-                            },
-                            label: {
-                                show: false
-                            }
-                        },
-                        regions: [{
-                            name: 'Sikeloi',
-                            tooltip: {
-                                formatter: 'Sikeloi',
-                                textStyle: { color: '#555' },
-                                backgroundColor: '#ccc'
-                            }
-                        }, {
-                            name: 'Sikanoi',
-                            tooltip: {
-                                formatter: 'Sikanoi',
-                                textStyle: { color: '#555' },
-                                backgroundColor: '#ccc'
-                            }
-                        }, {
-                            name: 'Elymoi',
-                            tooltip: {
-                                formatter: 'Elymoi',
-                                textStyle: { color: '#555' },
-                                backgroundColor: '#ccc'
-                            }
-                        }],
-                        z: 0
-                    }],
-                    // series: {
-                    //     type: 'map',
-                    //     selectedMode: 'multiple',
-                    //     coordinateSystem: 'geo',
-                    //     geoIndex: 0
-                    // }
-                };
-
-                var chart = testHelper.create(echarts, 'main_geo_svg_regions', {
-                    title: [
-                        'symbol and label use the same name in SVG.',
-                        'Hover each symbol and text, tooltip should be displayed.',
-                        'Hover the three area, tooltip should be displayed.',
-                        'Click, check **selected**.'
-                    ],
-                    option: option,
-                    info: {},
-                    infoKey: 'event',
-                    height: 500
-                });
-
-                listenAndPrintEvent(chart);
-
-                if (chart) {
-                    chart.on('georoam', function (params) {
-                        // console.log(params);
-                    });
-                }
-
-            });
-
-        });
-        </script>
 
 
     </body>

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