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:24 UTC
[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`.
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