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 2018/05/29 15:20:36 UTC
[incubator-echarts] 01/05: support svg geo (part I)
This is an automated email from the ASF dual-hosted git repository.
sushuang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
commit 68893b09910ed7b58d518e2b4513c2972aa869be
Author: sushuang <su...@gmail.com>
AuthorDate: Mon May 28 21:10:56 2018 +0800
support svg geo (part I)
---
src/component/helper/MapDraw.js | 85 ++++++++++++++++------
src/coord/geo/Geo.js | 126 +++++++++++----------------------
src/coord/geo/GeoModel.js | 5 +-
src/coord/geo/Region.js | 10 ++-
src/coord/geo/fix/diaoyuIsland.js | 16 ++---
src/coord/geo/fix/geoCoord.js | 8 +--
src/coord/geo/fix/nanhai.js | 6 +-
src/coord/geo/fix/textCoord.js | 8 +--
src/coord/geo/geoCreator.js | 80 +++++++--------------
src/coord/geo/geoJSONLoader.js | 94 +++++++++++++++++++++++++
src/coord/geo/geoSVGLoader.js | 143 ++++++++++++++++++++++++++++++++++++++
src/coord/geo/geoSourceManager.js | 126 +++++++++++++++++++++++++++++++++
src/coord/geo/mapDataStorage.js | 104 +++++++++++++++++++++++++++
src/echarts.js | 38 +++++-----
14 files changed, 647 insertions(+), 202 deletions(-)
diff --git a/src/component/helper/MapDraw.js b/src/component/helper/MapDraw.js
index 0d00e2d..9ea90d9 100644
--- a/src/component/helper/MapDraw.js
+++ b/src/component/helper/MapDraw.js
@@ -22,6 +22,8 @@ import RoamController from './RoamController';
import * as roamHelper from '../../component/helper/roamHelper';
import {onIrrelevantElement} from '../../component/helper/cursorHelper';
import * as graphic from '../../util/graphic';
+import geoSourceManager from '../../coord/geo/geoSourceManager';
+import {getUID} from '../../util/component';
function getFixedItemStyle(model, scale) {
var itemStyle = model.getItemStyle();
@@ -36,17 +38,17 @@ function getFixedItemStyle(model, scale) {
return itemStyle;
}
-function updateMapSelectHandler(mapDraw, mapOrGeoModel, group, api, fromView) {
- group.off('click');
- group.off('mousedown');
+function updateMapSelectHandler(mapDraw, mapOrGeoModel, regionsGroup, api, fromView) {
+ regionsGroup.off('click');
+ regionsGroup.off('mousedown');
if (mapOrGeoModel.get('selectedMode')) {
- group.on('mousedown', function () {
+ regionsGroup.on('mousedown', function () {
mapDraw._mouseDownFlag = true;
});
- group.on('click', function (e) {
+ regionsGroup.on('click', function (e) {
if (!mapDraw._mouseDownFlag) {
return;
}
@@ -73,14 +75,14 @@ function updateMapSelectHandler(mapDraw, mapOrGeoModel, group, api, fromView) {
api.dispatchAction(action);
- updateMapSelected(mapOrGeoModel, group);
+ updateMapSelected(mapOrGeoModel, regionsGroup);
});
}
}
-function updateMapSelected(mapOrGeoModel, group) {
+function updateMapSelected(mapOrGeoModel, regionsGroup) {
// FIXME
- group.eachChild(function (otherRegionEl) {
+ regionsGroup.eachChild(function (otherRegionEl) {
zrUtil.each(otherRegionEl.__regions, function (region) {
otherRegionEl.trigger(mapOrGeoModel.isSelected(region.name) ? 'emphasis' : 'normal');
});
@@ -97,6 +99,12 @@ function MapDraw(api, updateGroup) {
var group = new graphic.Group();
/**
+ * @type {string}
+ * @private
+ */
+ this.uid = getUID('ec_map_draw');
+
+ /**
* @type {module:echarts/component/helper/RoamController}
* @private
*/
@@ -127,6 +135,26 @@ function MapDraw(api, updateGroup) {
* @type {booelan}
*/
this._mouseDownFlag;
+
+ /**
+ * @type {string}
+ */
+ this._mapName;
+
+ /**
+ * @type {boolean}
+ */
+ this._initialized;
+
+ /**
+ * @type {module:zrender/container/Group}
+ */
+ group.add(this._regionsGroup = new graphic.Group());
+
+ /**
+ * @type {module:zrender/container/Group}
+ */
+ group.add(this._backgroundGroup = new graphic.Group());
}
MapDraw.prototype = {
@@ -148,23 +176,26 @@ MapDraw.prototype = {
var geo = mapOrGeoModel.coordinateSystem;
+ this._updateBackground(geo);
+
+ var regionsGroup = this._regionsGroup;
var group = this.group;
var scale = geo.scale;
- var groupNewProp = {
+ var transform = {
position: geo.position,
scale: scale
};
// No animation when first draw or in action
- if (!group.childAt(0) || payload) {
- group.attr(groupNewProp);
+ if (!regionsGroup.childAt(0) || payload) {
+ group.attr(transform);
}
else {
- graphic.updateProps(group, groupNewProp, mapOrGeoModel);
+ graphic.updateProps(group, transform, mapOrGeoModel);
}
- group.removeAll();
+ regionsGroup.removeAll();
var itemStyleAccessPath = ['itemStyle'];
var hoverItemStyleAccessPath = ['emphasis', 'itemStyle'];
@@ -306,22 +337,37 @@ MapDraw.prototype = {
{hoverSilentOnTouch: !!mapOrGeoModel.get('selectedMode')}
);
- group.add(regionGroup);
+ regionsGroup.add(regionGroup);
});
this._updateController(mapOrGeoModel, ecModel, api);
- updateMapSelectHandler(this, mapOrGeoModel, group, api, fromView);
+ updateMapSelectHandler(this, mapOrGeoModel, regionsGroup, api, fromView);
- updateMapSelected(mapOrGeoModel, group);
+ updateMapSelected(mapOrGeoModel, regionsGroup);
},
remove: function () {
- this.group.removeAll();
+ this._regionsGroup.removeAll();
+ this._backgroundGroup.removeAll();
this._controller.dispose();
+ this._mapName && geoSourceManager.removeGraphic(this._mapName, this.uid);
+ this._mapName = null;
this._controllerHost = {};
},
+ _updateBackground: function (geo) {
+ var mapName = geo.map;
+
+ if (this._mapName !== mapName) {
+ zrUtil.each(geoSourceManager.makeGraphic(mapName, this.uid), function (root) {
+ this._backgroundGroup.add(root);
+ }, this);
+ }
+
+ this._mapName = mapName;
+ },
+
_updateController: function (mapOrGeoModel, ecModel, api) {
var geo = mapOrGeoModel.coordinateSystem;
var controller = this._controller;
@@ -366,9 +412,8 @@ MapDraw.prototype = {
}));
if (this._updateGroup) {
- var group = this.group;
- var scale = group.scale;
- group.traverse(function (el) {
+ var scale = this.group.scale;
+ this._regionsGroup.traverse(function (el) {
if (el.type === 'text') {
el.attr('scale', [1 / scale[0], 1 / scale[1]]);
}
diff --git a/src/coord/geo/Geo.js b/src/coord/geo/Geo.js
index 83428af..8c6491c 100644
--- a/src/coord/geo/Geo.js
+++ b/src/coord/geo/Geo.js
@@ -19,33 +19,22 @@
import * as zrUtil from 'zrender/src/core/util';
import BoundingRect from 'zrender/src/core/BoundingRect';
-import parseGeoJson from './parseGeoJson';
import View from '../View';
+import geoSourceManager from './geoSourceManager';
-import fixNanhai from './fix/nanhai';
-import fixTextCoord from './fix/textCoord';
-import fixGeoCoord from './fix/geoCoord';
-import fixDiaoyuIsland from './fix/diaoyuIsland';
-
-// Geo fix functions
-var geoFixFuncs = [
- fixNanhai,
- fixTextCoord,
- fixGeoCoord,
- fixDiaoyuIsland
-];
/**
* [Geo description]
- * @param {string} name Geo name
+ * For backward compatibility, the orginal interface:
+ * `name, map, geoJson, specialAreas, nameMap` is kept.
+ *
+ * @param {string|Object} name
* @param {string} map Map type
- * @param {Object} geoJson
- * @param {Object} [specialAreas]
* Specify the positioned areas by left, top, width, height
* @param {Object.<string, string>} [nameMap]
* Specify name alias
*/
-function Geo(name, map, geoJson, specialAreas, nameMap) {
+function Geo(name, map, nameMap) {
View.call(this, name);
@@ -55,9 +44,20 @@ function Geo(name, map, geoJson, specialAreas, nameMap) {
*/
this.map = map;
- this._nameCoordMap = zrUtil.createHashMap();
+ var source = geoSourceManager.load(map, nameMap);
+
+ this._nameCoordMap = source.nameCoordMap;
+ this._regionsMap = source.nameCoordMap;
+
+ /**
+ * @readOnly
+ */
+ this.regions = source.regions;
- this.loadGeoJson(geoJson, specialAreas, nameMap);
+ /**
+ * @type {module:zrender/src/core/BoundingRect}
+ */
+ this._rect = source.boundingRect;
}
Geo.prototype = {
@@ -86,61 +86,23 @@ Geo.prototype = {
}
return false;
},
+
/**
- * @param {Object} geoJson
- * @param {Object} [specialAreas]
- * Specify the positioned areas by left, top, width, height
- * @param {Object.<string, string>} [nameMap]
- * Specify name alias
+ * @override
*/
- loadGeoJson: function (geoJson, specialAreas, nameMap) {
- // https://jsperf.com/try-catch-performance-overhead
- try {
- this.regions = geoJson ? parseGeoJson(geoJson) : [];
- }
- catch (e) {
- throw 'Invalid geoJson format\n' + e.message;
- }
- specialAreas = specialAreas || {};
- nameMap = nameMap || {};
- var regions = this.regions;
- var regionsMap = zrUtil.createHashMap();
- for (var i = 0; i < regions.length; i++) {
- var regionName = regions[i].name;
- // Try use the alias in nameMap
- regionName = nameMap.hasOwnProperty(regionName) ? nameMap[regionName] : regionName;
- regions[i].name = regionName;
-
- regionsMap.set(regionName, regions[i]);
- // Add geoJson
- this.addGeoCoord(regionName, regions[i].center);
-
- // Some area like Alaska in USA map needs to be tansformed
- // to look better
- var specialArea = specialAreas[regionName];
- if (specialArea) {
- regions[i].transformTo(
- specialArea.left, specialArea.top, specialArea.width, specialArea.height
- );
- }
- }
-
- this._regionsMap = regionsMap;
-
- this._rect = null;
-
- zrUtil.each(geoFixFuncs, function (fixFunc) {
- fixFunc(this);
- }, this);
- },
-
- // Overwrite
transformTo: function (x, y, width, height) {
var rect = this.getBoundingRect();
+ // FIXME
+ // Should not name it as invertLng.
+ var invertLng = this.invertLng;
+
rect = rect.clone();
- // Longitute is inverted
- rect.y = -rect.y - rect.height;
+
+ if (invertLng) {
+ // Longitute is inverted
+ rect.y = -rect.y - rect.height;
+ }
var rawTransformable = this._rawTransformable;
@@ -150,8 +112,10 @@ Geo.prototype = {
rawTransformable.decomposeTransform();
- var scale = rawTransformable.scale;
- scale[1] = -scale[1];
+ if (invertLng) {
+ var scale = rawTransformable.scale;
+ scale[1] = -scale[1];
+ }
rawTransformable.updateTransform();
@@ -193,21 +157,11 @@ Geo.prototype = {
return this._nameCoordMap.get(name);
},
- // Overwrite
+ /**
+ * @override
+ */
getBoundingRect: function () {
- if (this._rect) {
- return this._rect;
- }
- var rect;
-
- var regions = this.regions;
- for (var i = 0; i < regions.length; i++) {
- var regionRect = regions[i].getBoundingRect();
- rect = rect || regionRect.clone();
- rect.union(regionRect);
- }
- // FIXME Always return new ?
- return (this._rect = rect || new BoundingRect(0, 0, 0, 0));
+ return this._rect;
},
/**
@@ -227,12 +181,12 @@ Geo.prototype = {
},
/**
- * @inheritDoc
+ * @override
*/
convertToPixel: zrUtil.curry(doConvert, 'dataToPoint'),
/**
- * @inheritDoc
+ * @override
*/
convertFromPixel: zrUtil.curry(doConvert, 'pointToData')
diff --git a/src/coord/geo/GeoModel.js b/src/coord/geo/GeoModel.js
index 63f9257..fad9977 100644
--- a/src/coord/geo/GeoModel.js
+++ b/src/coord/geo/GeoModel.js
@@ -78,7 +78,9 @@ var GeoModel = ComponentModel.extend({
// Aspect is width / height. Inited to be geoJson bbox aspect
// This parameter is used for scale this aspect
- aspectScale: 0.75,
+ // If svg used, aspectScale is 1 by default.
+ // aspectScale: 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
@@ -86,7 +88,6 @@ var GeoModel = ComponentModel.extend({
// layoutCenter: [50%, 50%]
// layoutSize: 100
-
silent: false,
// Map type
diff --git a/src/coord/geo/Region.js b/src/coord/geo/Region.js
index 43d9cd6..4de33b1 100644
--- a/src/coord/geo/Region.js
+++ b/src/coord/geo/Region.js
@@ -27,7 +27,7 @@ import * as vec2 from 'zrender/src/core/vector';
import * as polygonContain from 'zrender/src/contain/polygon';
/**
- * @param {string} name
+ * @param {string|Region} name
* @param {Array} geometries
* @param {Array.<number>} cp
*/
@@ -168,6 +168,14 @@ Region.prototype = {
rect.x + rect.width / 2,
rect.y + rect.height / 2
];
+ },
+
+ cloneShallow: function (name) {
+ name == null && (name = this.name);
+ var newRegion = new Region(name, this.geometries, this.center);
+ newRegion._rect = this._rect;
+ newRegion.transformTo = null; // Simply avoid to be called.
+ return newRegion;
}
};
diff --git a/src/coord/geo/fix/diaoyuIsland.js b/src/coord/geo/fix/diaoyuIsland.js
index 22fcd77..87b3f57 100644
--- a/src/coord/geo/fix/diaoyuIsland.js
+++ b/src/coord/geo/fix/diaoyuIsland.js
@@ -34,15 +34,11 @@ var points = [
]
];
-export default function (geo) {
- if (geo.map === 'china') {
- for (var i = 0, len = geo.regions.length; i < len; ++i) {
- if (geo.regions[i].name === '台湾') {
- geo.regions[i].geometries.push({
- type: 'polygon',
- exterior: points[0]
- });
- }
- }
+export default function (mapType, region) {
+ if (mapType === 'china' && region.name === '台湾') {
+ region.geometries.push({
+ type: 'polygon',
+ exterior: points[0]
+ });
}
}
\ No newline at end of file
diff --git a/src/coord/geo/fix/geoCoord.js b/src/coord/geo/fix/geoCoord.js
index 69cc11a..b6e7080 100644
--- a/src/coord/geo/fix/geoCoord.js
+++ b/src/coord/geo/fix/geoCoord.js
@@ -17,21 +17,19 @@
* under the License.
*/
-import * as zrUtil from 'zrender/src/core/util';
-
var geoCoordMap = {
'Russia': [100, 60],
'United States': [-99, 38],
'United States of America': [-99, 38]
};
-export default function (geo) {
- zrUtil.each(geo.regions, function (region) {
+export default function (mapType, region) {
+ if (mapType === 'world') {
var geoCoord = geoCoordMap[region.name];
if (geoCoord) {
var cp = region.center;
cp[0] = geoCoord[0];
cp[1] = geoCoord[1];
}
- });
+ }
}
\ No newline at end of file
diff --git a/src/coord/geo/fix/nanhai.js b/src/coord/geo/fix/nanhai.js
index f59c529..4eabeaf 100644
--- a/src/coord/geo/fix/nanhai.js
+++ b/src/coord/geo/fix/nanhai.js
@@ -51,9 +51,9 @@ for (var i = 0; i < points.length; i++) {
}
}
-export default function (geo) {
- if (geo.map === 'china') {
- geo.regions.push(new Region(
+export default function (mapType, regions) {
+ if (mapType === 'china') {
+ regions.push(new Region(
'南海诸岛',
zrUtil.map(points, function (exterior) {
return {
diff --git a/src/coord/geo/fix/textCoord.js b/src/coord/geo/fix/textCoord.js
index c2b858a..3ec837c 100644
--- a/src/coord/geo/fix/textCoord.js
+++ b/src/coord/geo/fix/textCoord.js
@@ -17,8 +17,6 @@
* under the License.
*/
-import * as zrUtil from 'zrender/src/core/util';
-
var coordsOffsetMap = {
'南海诸岛' : [32, 80],
// 全国
@@ -29,13 +27,13 @@ var coordsOffsetMap = {
'天津': [5, 5]
};
-export default function (geo) {
- zrUtil.each(geo.regions, function (region) {
+export default function (mapType, region) {
+ if (mapType === 'china') {
var coordFix = coordsOffsetMap[region.name];
if (coordFix) {
var cp = region.center;
cp[0] += coordFix[0] / 10.5;
cp[1] += -coordFix[1] / (10.5 / 0.75);
}
- });
+ }
}
\ No newline at end of file
diff --git a/src/coord/geo/geoCreator.js b/src/coord/geo/geoCreator.js
index da94330..e740b65 100644
--- a/src/coord/geo/geoCreator.js
+++ b/src/coord/geo/geoCreator.js
@@ -23,6 +23,8 @@ import * as zrUtil from 'zrender/src/core/util';
import Geo from './Geo';
import * as layout from '../../util/layout';
import * as numberUtil from '../../util/number';
+import geoSourceManager from './geoSourceManager';
+import mapDataStorage from './mapDataStorage';
/**
* Resize method bound to the geo
@@ -55,8 +57,7 @@ function resizeGeo(geoModel, api) {
var viewWidth = api.getWidth();
var viewHeight = api.getHeight();
- var aspectScale = geoModel.get('aspectScale') || 0.75;
- var aspect = rect.width / rect.height * aspectScale;
+ var aspect = rect.width / rect.height * this.aspectScale;
var useCenterAndSize = false;
@@ -122,12 +123,6 @@ function setGeoCoords(geo, model) {
});
}
-if (__DEV__) {
- var mapNotExistsError = function (name) {
- console.error('Map ' + name + ' not exists. You can download map file on http://echarts.baidu.com/download-map.html');
- };
-}
-
var geoCreator = {
// For deciding which dimensions to use when creating list data
@@ -139,17 +134,8 @@ var geoCreator = {
// FIXME Create each time may be slow
ecModel.eachComponent('geo', function (geoModel, idx) {
var name = geoModel.get('map');
- var mapData = echarts.getMap(name);
- if (__DEV__) {
- if (!mapData) {
- mapNotExistsError(name);
- }
- }
- var geo = new Geo(
- name + idx, name,
- mapData && mapData.geoJson, mapData && mapData.specialAreas,
- geoModel.get('nameMap')
- );
+ var geo = new Geo(name + idx, name, geoModel.get('nameMap'));
+
geo.zoomLimit = geoModel.get('scaleLimit');
geoList.push(geo);
@@ -158,6 +144,20 @@ var geoCreator = {
geoModel.coordinateSystem = geo;
geo.model = geoModel;
+ // FIXME ???
+ var aspectScale = geoModel.get('aspectScale');
+ var invertLng = true;
+ var mapRecords = mapDataStorage.retrieveMap(name);
+ if (mapRecords && mapRecords[0] && mapRecords[0].type === 'svg') {
+ aspectScale == null && (aspectScale = 1);
+ invertLng = false;
+ }
+ else {
+ aspectScale == null && (aspectScale = 0.75);
+ }
+ geo.aspectScale = aspectScale;
+ geo.invertLng = invertLng;
+
// Inject resize method
geo.resize = resizeGeo;
@@ -184,21 +184,11 @@ var geoCreator = {
});
zrUtil.each(mapModelGroupBySeries, function (mapSeries, mapType) {
- var mapData = echarts.getMap(mapType);
- if (__DEV__) {
- if (!mapData) {
- mapNotExistsError(mapSeries[0].get('map'));
- }
- }
-
var nameMapList = zrUtil.map(mapSeries, function (singleMapSeries) {
return singleMapSeries.get('nameMap');
});
- var geo = new Geo(
- mapType, mapType,
- mapData && mapData.geoJson, mapData && mapData.specialAreas,
- zrUtil.mergeAll(nameMapList)
- );
+ var geo = new Geo(mapType, mapType, zrUtil.mergeAll(nameMapList));
+
geo.zoomLimit = zrUtil.retrieve.apply(null, zrUtil.map(mapSeries, function (singleMapSeries) {
return singleMapSeries.get('scaleLimit');
}));
@@ -229,34 +219,18 @@ var geoCreator = {
getFilledRegions: function (originRegionArr, mapName, nameMap) {
// Not use the original
var regionsArr = (originRegionArr || []).slice();
- nameMap = nameMap || {};
-
- var map = echarts.getMap(mapName);
- var geoJson = map && map.geoJson;
- if (!geoJson) {
- if (__DEV__) {
- mapNotExistsError(mapName);
- }
- return originRegionArr;
- }
var dataNameMap = zrUtil.createHashMap();
- var features = geoJson.features;
for (var i = 0; i < regionsArr.length; i++) {
dataNameMap.set(regionsArr[i].name, regionsArr[i]);
}
- for (var i = 0; i < features.length; i++) {
- var name = features[i].properties.name;
- if (!dataNameMap.get(name)) {
- if (nameMap.hasOwnProperty(name)) {
- name = nameMap[name];
- }
- regionsArr.push({
- name: name
- });
- }
- }
+ var source = geoSourceManager.load(mapName, nameMap);
+ zrUtil.each(source.regions, function (region) {
+ var name = region.name;
+ !dataNameMap.get(name) && regionsArr.push({name: name});
+ });
+
return regionsArr;
}
};
diff --git a/src/coord/geo/geoJSONLoader.js b/src/coord/geo/geoJSONLoader.js
new file mode 100644
index 0000000..5f0c05a
--- /dev/null
+++ b/src/coord/geo/geoJSONLoader.js
@@ -0,0 +1,94 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import {each} from 'zrender/src/core/util';
+import parseGeoJson from './parseGeoJson';
+import {makeInner} from '../../util/model';
+
+// Built-in GEO fixer.
+import fixNanhai from './fix/nanhai';
+import fixTextCoord from './fix/textCoord';
+import fixGeoCoord from './fix/geoCoord';
+import fixDiaoyuIsland from './fix/diaoyuIsland';
+
+var inner = makeInner();
+
+export default {
+
+ /**
+ * @param {string} mapName
+ * @param {Object} mapRecord {specialAreas, geoJSON}
+ * @return {Object} {regions, boundingRect}
+ */
+ load: function (mapName, mapRecord) {
+
+ var parsed = inner(mapRecord).parsed;
+
+ if (parsed) {
+ return parsed;
+ }
+
+ var specialAreas = mapRecord.specialAreas || {};
+ var geoJSON = mapRecord.geoJSON;
+ var regions;
+
+ // https://jsperf.com/try-catch-performance-overhead
+ try {
+ regions = geoJSON ? parseGeoJson(geoJSON) : [];
+ }
+ catch (e) {
+ throw new Error('Invalid geoJson format\n' + e.message);
+ }
+
+ each(regions, function (region) {
+ var regionName = region.name;
+
+ fixTextCoord(mapName, region);
+ fixGeoCoord(mapName, region);
+ fixDiaoyuIsland(mapName, region);
+
+ // Some area like Alaska in USA map needs to be tansformed
+ // to look better
+ var specialArea = specialAreas[regionName];
+ if (specialArea) {
+ region.transformTo(
+ specialArea.left, specialArea.top, specialArea.width, specialArea.height
+ );
+ }
+ });
+
+ fixNanhai(mapName, regions);
+
+ return (inner(mapRecord).parsed = {
+ regions: regions,
+ boundingRect: getBoundingRect(regions)
+ });
+ }
+};
+
+function getBoundingRect(regions) {
+ var rect;
+ for (var i = 0; i < regions.length; i++) {
+ var regionRect = regions[i].getBoundingRect();
+ rect = rect || regionRect.clone();
+ rect.union(regionRect);
+ }
+ return rect;
+}
+
diff --git a/src/coord/geo/geoSVGLoader.js b/src/coord/geo/geoSVGLoader.js
new file mode 100644
index 0000000..98eab03
--- /dev/null
+++ b/src/coord/geo/geoSVGLoader.js
@@ -0,0 +1,143 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import {parseSVG, makeViewBoxTransform} from 'zrender/src/tool/parseSVG';
+import Group from 'zrender/src/container/Group';
+import Rect from 'zrender/src/graphic/shape/Rect';
+import {assert, createHashMap} from 'zrender/src/core/util';
+import BoundingRect from 'zrender/src/core/BoundingRect';
+import {makeInner} from '../../util/model';
+
+var inner = makeInner();
+
+export default {
+
+ /**
+ * @param {string} mapName
+ * @param {Object} mapRecord {specialAreas, geoJSON}
+ * @return {Object} {root, boundingRect}
+ */
+ load: function (mapName, mapRecord) {
+ var originRoot = inner(mapRecord).originRoot;
+ if (originRoot) {
+ return {
+ root: originRoot,
+ boundingRect: inner(mapRecord).boundingRect
+ };
+ }
+
+ var graphic = buildGraphic(mapRecord);
+
+ inner(mapRecord).originRoot = graphic.root;
+ inner(mapRecord).boundingRect = graphic.boundingRect;
+
+ return graphic;
+ },
+
+ makeGraphic: function (mapName, mapRecord, hostKey) {
+ // For performance consideration (in large SVG), graphic only maked
+ // when necessary and reuse them according to hostKey.
+ var field = inner(mapRecord);
+ var rootMap = field.rootMap || (field.rootMap = createHashMap());
+
+ var root = rootMap.get(hostKey);
+ if (root) {
+ return root;
+ }
+
+ var originRoot = field.originRoot;
+ var boundingRect = field.boundingRect;
+
+ // For performance, if originRoot is not used by a view,
+ // assign it to a view, but not reproduce graphic elements.
+ if (!field.originRootHostKey) {
+ field.originRootHostKey = hostKey;
+ root = originRoot;
+ }
+ else {
+ root = buildGraphic(mapRecord, boundingRect).root;
+ }
+
+ return rootMap.set(hostKey, root);
+ },
+
+ removeGraphic: function (mapName, mapRecord, hostKey) {
+ var field = inner(mapRecord);
+ var rootMap = field.rootMap;
+ rootMap && rootMap.removeKey(hostKey);
+ if (hostKey === field.originRootHostKey) {
+ field.originRootHostKey = null;
+ }
+ }
+};
+
+function buildGraphic(mapRecord, boundingRect) {
+ var svgXML = mapRecord.svgXML;
+ var result;
+ var root;
+
+ try {
+ result = svgXML && parseSVG(svgXML, {
+ ignoreViewBox: true,
+ ignoreRootClip: true
+ }) || {};
+ root = result.root;
+ assert(root != null);
+ }
+ catch (e) {
+ throw new Error('Invalid svg format\n' + e.message);
+ }
+
+ var svgWidth = result.width;
+ var svgHeight = result.height;
+ var viewBoxRect = result.viewBoxRect;
+
+ if (!boundingRect) {
+ boundingRect = (svgWidth == null || svgHeight == null)
+ // If svg width / height not specified, calculate
+ // bounding rect as the width / height
+ ? root.getBoundingRect()
+ : new BoundingRect(0, 0, 0, 0);
+
+ if (svgWidth != null) {
+ boundingRect.width = svgWidth;
+ }
+ if (svgHeight != null) {
+ boundingRect.height = svgHeight;
+ }
+ }
+
+ if (viewBoxRect) {
+ var viewBoxTransform = makeViewBoxTransform(viewBoxRect, boundingRect.width, boundingRect.height);
+ var elRoot = root;
+ root = new Group();
+ root.add(elRoot);
+ elRoot.scale = viewBoxTransform.scale;
+ elRoot.position = viewBoxTransform.position;
+ }
+
+ root.setClipPath(new Rect({
+ shape: boundingRect.plain()
+ }));
+
+ return {
+ root: root,
+ boundingRect: boundingRect
+ };
+}
diff --git a/src/coord/geo/geoSourceManager.js b/src/coord/geo/geoSourceManager.js
new file mode 100644
index 0000000..53ce1f8
--- /dev/null
+++ b/src/coord/geo/geoSourceManager.js
@@ -0,0 +1,126 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import {__DEV__} from '../../config';
+import {each, createHashMap} from 'zrender/src/core/util';
+import mapDataStorage from './mapDataStorage';
+import geoJSONLoader from './geoJSONLoader';
+import geoSVGLoader from './geoSVGLoader';
+import BoundingRect from 'zrender/src/core/BoundingRect';
+
+var loaders = {
+ geoJSON: geoJSONLoader,
+ svg: geoSVGLoader
+};
+
+export default {
+
+ /**
+ * @param {string} mapName
+ * @param {Object} nameMap
+ * @return {Object} source {regions, regionsMap, nameCoordMap, boundingRect}
+ */
+ load: function (mapName, nameMap) {
+ var regions = [];
+ var regionsMap = createHashMap();
+ var nameCoordMap = createHashMap();
+ var boundingRect;
+ var mapRecords = retrieveMap(mapName);
+
+ each(mapRecords, function (record) {
+ var singleSource = loaders[record.type].load(mapName, record);
+
+ each(singleSource.regions, function (region) {
+ var regionName = region.name;
+
+ // Try use the alias in geoNameMap
+ if (nameMap && nameMap.hasOwnProperty(regionName)) {
+ region = region.cloneShallow(regionName = nameMap[regionName]);
+ }
+
+ regions.push(region);
+ regionsMap.set(regionName, region);
+ nameCoordMap.set(regionName, region.center);
+ });
+
+ var rect = singleSource.boundingRect;
+ if (rect) {
+ boundingRect
+ ? boundingRect.union(rect)
+ : (boundingRect = rect.clone());
+ }
+ });
+
+ return {
+ regions: regions,
+ regionsMap: regionsMap,
+ nameCoordMap: nameCoordMap,
+ // FIXME Always return new ?
+ boundingRect: boundingRect || new BoundingRect(0, 0, 0, 0)
+ };
+ },
+
+ /**
+ * @param {string} mapName
+ * @param {string} hostKey For cache.
+ * @return {Array.<module:zrender/Element>} Roots.
+ */
+ makeGraphic: makeInvoker('makeGraphic'),
+
+ /**
+ * @param {string} mapName
+ * @param {string} hostKey For cache.
+ */
+ removeGraphic: makeInvoker('removeGraphic')
+};
+
+function makeInvoker(methodName) {
+ return function (mapName, hostKey) {
+ var mapRecords = retrieveMap(mapName);
+ var results = [];
+
+ each(mapRecords, function (record) {
+ var method = loaders[record.type][methodName];
+ method && results.push(method(mapName, record, hostKey));
+ });
+
+ return results;
+ };
+}
+
+function mapNotExistsError(mapName) {
+ if (__DEV__) {
+ console.error(
+ 'Map ' + mapName + ' not exists. You can download map file on http://echarts.baidu.com/download-map.html'
+ );
+ }
+}
+
+function retrieveMap(mapName) {
+ var mapRecords = mapDataStorage.retrieveMap(mapName) || [];
+
+ if (__DEV__) {
+ if (!mapRecords.length) {
+ mapNotExistsError(mapName);
+ }
+ }
+
+ return mapRecords;
+}
+
diff --git a/src/coord/geo/mapDataStorage.js b/src/coord/geo/mapDataStorage.js
new file mode 100644
index 0000000..8f6de57
--- /dev/null
+++ b/src/coord/geo/mapDataStorage.js
@@ -0,0 +1,104 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import {__DEV__} from '../../config';
+import {createHashMap, isString, isArray, each, assert} from 'zrender/src/core/util';
+import {parseXML} from 'zrender/src/tool/parseSVG';
+
+
+var storage = createHashMap();
+
+// For minimize the code size of common echarts package,
+// do not put too much logic in this module.
+
+export default {
+
+ // The format of record: see `echarts.registerMap`.
+ // Compatible with previous `echarts.registerMap`.
+ registerMap: function (mapName, rawGeoJson, rawSpecialAreas) {
+
+ var records;
+
+ if (isArray(rawGeoJson)) {
+ records = rawGeoJson;
+ }
+ else if (rawGeoJson.svg) {
+ records = [{
+ type: 'svg',
+ source: rawGeoJson.svg,
+ specialAreas: rawGeoJson.specialAreas
+ }];
+ }
+ else {
+ // Backward compatibility.
+ if (rawGeoJson.geoJson && !rawGeoJson.features) {
+ rawSpecialAreas = rawGeoJson.specialAreas;
+ rawGeoJson = rawGeoJson.geoJson;
+ }
+ records = [{
+ type: 'geoJSON',
+ source: rawGeoJson,
+ specialAreas: rawSpecialAreas
+ }];
+ }
+
+ each(records, function (record) {
+ var type = record.type;
+ type === 'geoJson' && (type = record.type = 'geoJSON');
+
+ var parse = parsers[type];
+
+ if (__DEV__) {
+ assert(parse, 'Illegal map type: ' + type);
+ }
+
+ parse(record);
+ });
+
+ return storage.set(mapName, records);
+ },
+
+ retrieveMap: function (mapName) {
+ return storage.get(mapName);
+ }
+
+};
+
+var parsers = {
+
+ geoJSON: function (record) {
+ var source = record.source;
+ record.geoJSON = !isString(source)
+ ? source
+ : (typeof JSON !== 'undefined' && JSON.parse)
+ ? JSON.parse(source)
+ : (new Function('return (' + source + ');'))();
+ },
+
+ // Only perform parse to XML object here, which might be time
+ // consiming for large SVG.
+ // Although convert XML to zrender element is also time consiming,
+ // if we do it here, the clone of zrender elements has to be
+ // required. So we do it once for each geo instance, util real
+ // performance issues call for optimizing it.
+ svg: function (record) {
+ record.svgXML = parseXML(record.source);
+ }
+
+};
diff --git a/src/echarts.js b/src/echarts.js
index 019ef6b..dec7148 100644
--- a/src/echarts.js
+++ b/src/echarts.js
@@ -43,6 +43,7 @@ import Scheduler from './stream/Scheduler';
import lightTheme from './theme/light';
import darkTheme from './theme/dark';
import './component/dataset';
+import mapDataStorage from './coord/geo/mapDataStorage';
var assert = zrUtil.assert;
var each = zrUtil.each;
@@ -1720,8 +1721,6 @@ var idBase = new Date() - 0;
var groupIdBase = new Date() - 0;
var DOM_ATTRIBUTE_KEY = '_echarts_instance_';
-var mapDataStores = {};
-
function enableConnect(chart) {
var STATUS_PENDING = 0;
var STATUS_UPDATING = 1;
@@ -2115,10 +2114,10 @@ export function setCanvasCreator(creator) {
/**
* @param {string} mapName
- * @param {Object|string} geoJson
+ * @param {Array.<Object>|Object|string} geoJson
* @param {Object} [specialAreas]
*
- * @example
+ * @example GeoJSON
* $.get('USA.json', function (geoJson) {
* echarts.registerMap('USA', geoJson);
* // Or
@@ -2127,20 +2126,20 @@ export function setCanvasCreator(creator) {
* specialAreas: {}
* })
* });
+ *
+ * $.get('airport.svg', function (svg) {
+ * echarts.registerMap('airport', {
+ * svg: svg
+ * }
+ * });
+ *
+ * echarts.registerMap('eu', [
+ * {svg: eu-topographic.svg},
+ * {geoJSON: eu.json}
+ * ])
*/
export function registerMap(mapName, geoJson, specialAreas) {
- if (geoJson.geoJson && !geoJson.features) {
- specialAreas = geoJson.specialAreas;
- geoJson = geoJson.geoJson;
- }
- if (typeof geoJson === 'string') {
- geoJson = (typeof JSON !== 'undefined' && JSON.parse)
- ? JSON.parse(geoJson) : (new Function('return (' + geoJson + ');'))();
- }
- mapDataStores[mapName] = {
- geoJson: geoJson,
- specialAreas: specialAreas
- };
+ mapDataStorage.registerMap(mapName, geoJson, specialAreas);
}
/**
@@ -2148,7 +2147,12 @@ export function registerMap(mapName, geoJson, specialAreas) {
* @return {Object}
*/
export function getMap(mapName) {
- return mapDataStores[mapName];
+ // For backward compatibility, only return the first one.
+ var records = mapDataStorage.retrieveMap(mapName);
+ return records && records[0] && {
+ geoJson: records[0].geoJSON,
+ specialAreas: records[0].specialAreas
+ };
}
registerVisual(PRIORITY_VISUAL_GLOBAL, seriesColor);
--
To stop receiving notification emails like this one, please contact
sushuang@apache.org.
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org