123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- /*
- * 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.
- */
- /**
- * AUTO-GENERATED FILE. DO NOT MODIFY.
- */
- /*
- * 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/lib/tool/parseSVG.js';
- import Group from 'zrender/lib/graphic/Group.js';
- import Rect from 'zrender/lib/graphic/shape/Rect.js';
- import { assert, createHashMap, each } from 'zrender/lib/core/util.js';
- import BoundingRect from 'zrender/lib/core/BoundingRect.js';
- import { parseXML } from 'zrender/lib/tool/parseXML.js';
- import { GeoSVGRegion } from './Region.js';
- /**
- * "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.
- */
- var REGION_AVAILABLE_SVG_TAG_MAP = createHashMap(['rect', 'circle', 'line', 'ellipse', 'polygon', 'polyline', 'path', // <text> <tspan> are also enabled because 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']);
- var GeoSVGResource =
- /** @class */
- function () {
- function GeoSVGResource(mapName, svg) {
- this.type = 'geoSVG'; // All used graphics. key: hostKey, value: root
- this._usedGraphicMap = createHashMap(); // All unused graphics.
- this._freedGraphics = [];
- this._mapName = mapName; // Only perform parse to XML object here, which might be time
- // consiming for large SVG.
- // Although convert XML to zrender element is also time consiming,
- // if we do it here, the clone of zrender elements has to be
- // required. So we do it once for each geo instance, util real
- // performance issues call for optimizing it.
- this._parsedXML = parseXML(svg);
- }
- GeoSVGResource.prototype.load = function ()
- /* nameMap: NameMap */
- {
- // In the "load" stage, graphic need to be built to
- // get boundingRect for geo coordinate system.
- var 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 = this._buildGraphic(this._parsedXML);
- this._freedGraphics.push(firstGraphic);
- this._boundingRect = this._firstGraphic.boundingRect.clone(); // PENDING: `nameMap` will not be supported until some real requirement come.
- // if (nameMap) {
- // named = applyNameMap(named, nameMap);
- // }
- var _a = createRegions(firstGraphic.named),
- regions = _a.regions,
- regionsMap = _a.regionsMap;
- this._regions = regions;
- this._regionsMap = regionsMap;
- }
- return {
- boundingRect: this._boundingRect,
- regions: this._regions,
- regionsMap: this._regionsMap
- };
- };
- GeoSVGResource.prototype._buildGraphic = function (svgXML) {
- var result;
- var rootFromParse;
- try {
- result = svgXML && parseSVG(svgXML, {
- ignoreViewBox: true,
- ignoreRootClip: true
- }) || {};
- rootFromParse = result.root;
- assert(rootFromParse != null);
- } catch (e) {
- throw new Error('Invalid svg format\n' + e.message);
- } // Note: we keep the covenant that the root has no transform. So always add an extra root.
- var root = new Group();
- root.add(rootFromParse);
- root.isGeoSVGGraphicRoot = true; // [THE_RULE_OF_VIEWPORT_AND_VIEWBOX]
- //
- // Consider: `<svg width="..." height="..." viewBox="...">`
- // - the `width/height` we call it `svgWidth/svgHeight` for short.
- // - `(0, 0, svgWidth, svgHeight)` defines the viewport of the SVG, or say,
- // "viewport boundingRect", or `boundingRect` for short.
- // - `viewBox` defines the transform from the real content ot the viewport.
- // `viewBox` has the same unit as the content of SVG.
- // If `viewBox` exists, a transform is defined, so the unit of `svgWidth/svgHeight` become
- // different from the content of SVG. Otherwise, they are the same.
- //
- // If both `svgWidth/svgHeight/viewBox` are specified in a SVG file, the transform rule will be:
- // 0. `boundingRect` is `(0, 0, svgWidth, svgHeight)`. Set it to Geo['_rect'] (View['_rect']).
- // 1. Make a transform from `viewBox` to `boundingRect`.
- // Note: only support `preserveAspectRatio 'xMidYMid'` here. That is, this transform will preserve
- // the aspect ratio.
- // 2. Make a transform from boundingRect to Geo['_viewRect'] (View['_viewRect'])
- // (`Geo`/`View` will do this job).
- // Note: this transform might not preserve aspect radio, which depending on how users specify
- // viewRect in echarts option (e.g., `geo.left/top/width/height` will not preserve aspect ratio,
- // but `geo.layoutCenter/layoutSize` will preserve aspect ratio).
- //
- // If `svgWidth/svgHeight` not specified, we use `viewBox` as the `boundingRect` to make the SVG
- // layout look good.
- //
- // If neither `svgWidth/svgHeight` nor `viewBox` are not specified, we calculate the boundingRect
- // of the SVG content and use them to make SVG layout look good.
- var svgWidth = result.width;
- var svgHeight = result.height;
- var viewBoxRect = result.viewBoxRect;
- var boundingRect = this._boundingRect;
- if (!boundingRect) {
- var bRectX = void 0;
- var bRectY = void 0;
- var bRectWidth = void 0;
- var bRectHeight = void 0;
- if (svgWidth != null) {
- bRectX = 0;
- bRectWidth = svgWidth;
- } else if (viewBoxRect) {
- bRectX = viewBoxRect.x;
- bRectWidth = viewBoxRect.width;
- }
- if (svgHeight != null) {
- bRectY = 0;
- bRectHeight = svgHeight;
- } else if (viewBoxRect) {
- bRectY = viewBoxRect.y;
- bRectHeight = viewBoxRect.height;
- } // If both viewBox and svgWidth/svgHeight not specified,
- // we have to determine how to layout those element to make them look good.
- if (bRectX == null || bRectY == null) {
- var calculatedBoundingRect = rootFromParse.getBoundingRect();
- if (bRectX == null) {
- bRectX = calculatedBoundingRect.x;
- bRectWidth = calculatedBoundingRect.width;
- }
- if (bRectY == null) {
- bRectY = calculatedBoundingRect.y;
- bRectHeight = calculatedBoundingRect.height;
- }
- }
- boundingRect = this._boundingRect = new BoundingRect(bRectX, bRectY, bRectWidth, bRectHeight);
- }
- if (viewBoxRect) {
- var viewBoxTransform = makeViewBoxTransform(viewBoxRect, boundingRect); // Only support `preserveAspectRatio 'xMidYMid'`
- rootFromParse.scaleX = rootFromParse.scaleY = viewBoxTransform.scale;
- rootFromParse.x = viewBoxTransform.x;
- rootFromParse.y = viewBoxTransform.y;
- } // SVG needs to clip based on `viewBox`. And some SVG files really rely on this feature.
- // They do not strictly confine all of the content inside a display rect, but deliberately
- // use a `viewBox` to define a displayable rect.
- // PENDING:
- // The drawback of the `setClipPath` here is: the region label (genereted by echarts) near the
- // edge might also be clipped, because region labels are put as `textContent` of the SVG path.
- root.setClipPath(new Rect({
- shape: boundingRect.plain()
- }));
- var named = [];
- each(result.named, function (namedItem) {
- if (REGION_AVAILABLE_SVG_TAG_MAP.get(namedItem.svgNodeTagLower) != null) {
- named.push(namedItem);
- setSilent(namedItem.el);
- }
- });
- return {
- root: root,
- boundingRect: boundingRect,
- named: named
- };
- };
- /**
- * Consider:
- * (1) One graphic element can not be shared by different `geoView` running simultaneously.
- * Notice, also need to consider multiple echarts instances share a `mapRecord`.
- * (2) Converting SVG to graphic elements is time consuming.
- * (3) In the current architecture, `load` should be called frequently to get boundingRect,
- * and it is called without view info.
- * So we maintain graphic elements in this module, and enables `view` to use/return these
- * graphics from/to the pool with it's uid.
- */
- GeoSVGResource.prototype.useGraphic = function (hostKey
- /* , nameMap: NameMap */
- ) {
- var usedRootMap = this._usedGraphicMap;
- var svgGraphic = usedRootMap.get(hostKey);
- if (svgGraphic) {
- return svgGraphic;
- }
- svgGraphic = this._freedGraphics.pop() // use the first boundingRect to avoid duplicated boundingRect calculation.
- || this._buildGraphic(this._parsedXML);
- usedRootMap.set(hostKey, svgGraphic); // PENDING: `nameMap` will not be supported until some real requirement come.
- // `nameMap` can only be obtained from echarts option.
- // The original `named` must not be modified.
- // if (nameMap) {
- // svgGraphic = extend({}, svgGraphic);
- // svgGraphic.named = applyNameMap(svgGraphic.named, nameMap);
- // }
- return svgGraphic;
- };
- GeoSVGResource.prototype.freeGraphic = function (hostKey) {
- var usedRootMap = this._usedGraphicMap;
- var svgGraphic = usedRootMap.get(hostKey);
- if (svgGraphic) {
- usedRootMap.removeKey(hostKey);
- this._freedGraphics.push(svgGraphic);
- }
- };
- return GeoSVGResource;
- }();
- export { GeoSVGResource };
- function setSilent(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(function (child) {
- child.silent = false;
- });
- }
- }
- function createRegions(named) {
- var regions = [];
- var regionsMap = createHashMap(); // Create resions only for the first graphic.
- each(named, function (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;
- }
- var 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: regions,
- regionsMap: regionsMap
- };
- } // PENDING: `nameMap` will not be supported until some real requirement come.
- // /**
- // * Use the alias in geoNameMap.
- // * The input `named` must not be modified.
- // */
- // function applyNameMap(
- // named: GeoSVGGraphicRecord['named'],
- // nameMap: NameMap
- // ): 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);
- // regionGraphic.name = name;
- // }
- // result.push(regionGraphic);
- // }
- // return result;
- // }
|