提交 46c4c3f2 编写于 作者: O Ovilia

Merge branch 'master' of github.com:ecomfe/echarts

# Only support regexp, testing against each relative file path
# based on the echart base directory. And the pattern should
# match the relative path completely.
# Do not use wildcard. Although Apache Rat v0.12 implemented
# it, it probably does not do it right, where patterns are
# parsed as Regex firstly but do not catch the exception.
node_modules
.*\.git
.*\.github
.*\.editorconfig
.*\.gitignore
.*\.jshintrc$
.*\.jshintrc
.*\.jshintrc-dist
.*\.npmignore
.*\.rat-excludes
.*\.ratignore
.*\.headerignore
.*\.DS_Store
.*\.idea
.*rat\.iml
......@@ -24,6 +22,7 @@ DISCLAIMER
NOTICE
KEYS
LICENSE
LICENSE-.+
licenses
map/js
map/json
......@@ -36,8 +35,6 @@ test/lib/countup\.js
.*jquery\.min\.js
.*rollup\.browser\.js
.*configure
.*IAxisPointer
.*ICoordinateSystem
.+\.json
.+\.map
.+\.gexf
......
......@@ -213,46 +213,12 @@ notices and license terms. Your use of the source code for these
subcomponents is subject to the terms and conditions of the following
licenses.
BSD 3-Clause (zrender):
The following files embed [zrender](https://github.com/ecomfe/zrender) BSD 3-Clause:
`dist/echarts-en.common.js`,
`dist/echarts-en.common.min.js`,
`dist/echarts-en.js`,
`dist/echarts-en.min.js`,
`dist/echarts-en.simple.js`,
`dist/echarts-en.simple.min.js`,
`dist/echarts.common.js`,
`dist/echarts.common.min.js`,
`dist/echarts.js`,
`dist/echarts.min.js`,
`dist/echarts.simple.js`,
`dist/echarts.simple.min.js`,
See `licenses/LICENSE-zrender` for details of the license.
BSD 3-Clause (d3.js):
The following files embed [d3.js](https://github.com/d3/d3) BSD 3-Clause:
`src/chart/treemap/treemapLayout.js`,
`src/chart/tree/layoutHelper.js`,
`src/chart/graph/forceHelper.js`,
`src/util/array/nest.js`,
`src/util/number.js`,
`src/scale/Time.js`,
`lib/chart/treemap/treemapLayout.js`,
`lib/chart/tree/layoutHelper.js`,
`lib/chart/graph/forceHelper.js`,
`lib/util/array/nest.js`,
`lib/scale/Time.js`,
`extension-src/dataTool/quantile.js`,
`extension/dataTool/quantile.js`,
`dist/echarts-en.common.js`,
`dist/echarts-en.common.min.js`,
`dist/echarts-en.js`,
`dist/echarts-en.min.js`,
`dist/echarts-en.simple.js`,
`dist/echarts-en.simple.min.js`,
`dist/echarts.common.js`,
`dist/echarts.common.min.js`,
`dist/echarts.js`,
`dist/echarts.min.js`,
`dist/echarts.simple.js`,
`dist/echarts.simple.min.js`,
See `licenses/LICENSE-d3` for details of the license.
......@@ -150,3 +150,10 @@ Check this tutorial [Create Custom Build of ECharts](https://ecomfe.github.io/ec
## License
ECharts is available under the Apache License V2.
## Reference Paper
Deqing Li, Honghui Mei, Yi Shen, Shuang Su, Wenli Zhang, Junting Wang, Ming Zu, Wei Chen.
[ECharts: A Declarative Framework for Rapid Construction of Wed-based Visualization](http://www.cad.zju.edu.cn/home/vagblog/VAG_Work/echarts.pdf).
Visual Informatics, 2018.
......@@ -17,18 +17,16 @@
* under the License.
*/
/**
* For consistency, we use `.rat-excludes` for both Apache Rat and this tool.
* In the `.rat-excludes`, each line is a pattern in RegExp.
* all relative path (based on the echarts base directory) is tected.
* The pattern should match the relative path completely.
*/
const fs = require('fs');
const preamble = require('./preamble');
const pathTool = require('path');
const {color} = require('zrender/build/helper');
const excludesPath = pathTool.join(__dirname, '../.rat-excludes');
// In the `.headerignore`, each line is a pattern in RegExp.
// all relative path (based on the echarts base directory) is tested.
// The pattern should match the relative path completely.
const excludesPath = pathTool.join(__dirname, '../.headerignore');
const ecBasePath = pathTool.join(__dirname, '../');
const isVerbose = process.argv[2] === '--verbose';
......
......@@ -160,7 +160,8 @@ const licenseReg = [
{name: 'LGPL', reg: /LGPL/},
{name: 'GPL', reg: /GPL/},
{name: 'Mozilla', reg: /mozilla public/i},
{name: 'MIT', reg: /mit license/i}
{name: 'MIT', reg: /mit license/i},
{name: 'BSD-d3', reg: /Copyright\s+\(c\)\s+2010-2015,\s+Michael\s+Bostock/i}
];
function extractLicense(fileStr, fileExt) {
......
#!/bin/sh
# 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.
basePath=$(cd `dirname $0`; pwd)
ecPath=${basePath}/../..
runRatSrcDir=${ecPath}/build/rat/src
javac -classpath ${ecPath}/build/rat/javassist.jar ${runRatSrcDir}/RunRat.java
jar cfmv ${ecPath}/build/rat/runrat.jar ${runRatSrcDir}/MANIFEST.txt -C ${runRatSrcDir} RunRat.class
#!/bin/sh
# 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.
# RunRat is used to call Apache Rat.
#
# (1) Usage:
#
# Check all:
# ```shell
# java -jar ${ecPath}/build/rat/runrat.jar
# ```
#
# Get help:
# ```shell
# java -jar ${ecPath}/build/rat/runrat.jar --help
# ```
#
# Notice that most of the arguments is the same as Apache Rat,
# only `--dir` and `--exclude` should not be specified.
#
# Ohter feature of Apache Rat:
# ```shell
# java -jar ${ecPath}/build/rat/runrat.jar [option]
# ```
#
#
# (2) Why call Apache Rat via `RunRat`?
#
# Because Apache Rat only support specifying file name (in regexp or wildcard) in its
# "exclude" file, but not support specifying file path (in regexp or wildcard), which
# is commonly necessary in the "ignore/exclude" file of this kind of features.
#
# For example:
# the file path "aaa/lib" (with slash) is not supported. But if only specifying "lib",
# all of the directories "lib" are excluded, which is not expected.
#
# (See `org.apache.rat.walker.Walker#isIgnore` for details, where the file `dir` is not
# actually used by `org.apache.commons.io.filefilter.RegexFileFilter#accept` and
# `org.apache.commons.io.filefilter.WildcardFileFilter#accept`.)
# So use this tool as a workaround.
basePath=$(cd `dirname $0`; pwd)
ecPath=${basePath}/../..
java -jar ${ecPath}/build/rat/runrat.jar
Main-Class: RunRat
Class-Path: ./ ./javassist.jar
/*
* 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.
*/
/**
* RunRat is used to call Apache Rat.
*
*
* (1) Usage:
*
* Prepare, in command line:
* ```shell
* cd ${echartsBaseDir}/build/rat
* ```
*
* Check all:
* ```shell
* java RunRat
* ```
*
* Get help:
* ```shell
* java RunRat --help
* ```
*
* Notice that most of the arguments is the same as Apache Rat,
* only `--dir` and `--exclude` should not be specified.
*
* Ohter feature of Apache Rat:
* ```shell
* java RunRat [option]
* ```
*
* Rebuild:
* ```shell
* javac RunRat.java
* ```
*
*
* (2) Why call Apache Rat via `RunRat`?
*
* Because Apache Rat only support specifying file name (in regexp or wildcard) in its
* "exclude" file, but not support specifying file path (in regexp or wildcard), which
* is commonly necessary in the "ignore/exclude" file of this kind of features.
*
* For example:
* the file path "aaa/lib" (with slash) is not supported. But if only specifying "lib",
* all of the directories "lib" are excluded, which is not expected.
*
* (See `org.apache.rat.walker.Walker#isIgnore` for details, where the file `dir` is not
* actually used by `org.apache.commons.io.filefilter.RegexFileFilter#accept` and
* `org.apache.commons.io.filefilter.WildcardFileFilter#accept`.)
*
* So use this tool as a workaround.
*/
import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Loader;
public class RunRat {
private String defaultRatMainClassName = "org.apache.rat.Report";
private Method ratReportMainMethod;
private File ecBaseDir;
private ArrayList<String> ratArgList = new ArrayList<String>();
public static void main(String[] args) throws Exception {
RunRat runRat = new RunRat();
runRat.prepareArgs(args);
runRat.prepareLibs(args);
runRat.callRat();
}
private void prepareArgs(String[] args) throws IllegalArgumentException {
boolean reportTplSpecified = false;
for (int i = 0; i < args.length; ) {
String argStr = args[i];
if (argStr.equals("--dir")
|| argStr.equals("-d")
|| argStr.equals("-e")
|| argStr.equals("--exclude")
|| argStr.equals("-E")
|| argStr.equals("-exclude-file")
) {
throw new IllegalArgumentException(argStr + " should not be specified!");
}
if (argStr.equals("-s") || argStr.equals("--stylesheet")) {
reportTplSpecified = true;
}
if (argStr.equals("--ec-base")) {
this.ecBaseDir = new File(args[i + 1]);
i += 2;
}
else {
this.ratArgList.add(argStr);
i++;
}
}
if (this.ecBaseDir == null) {
this.ecBaseDir = new File(new File(
this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath()
).getParent() + "/../..");
}
this.ratArgList.add("--dir");
this.ratArgList.add(this.ecBaseDir.getPath());
this.ratArgList.add("--exclude-file");
this.ratArgList.add(this.ecBaseDir.getPath() + "/.rat-excludes");
if (!reportTplSpecified) {
this.ratArgList.add("-s");
this.ratArgList.add(this.ecBaseDir.getPath() + "/build/rat/src/report.xsl");
}
}
private void prepareLibs(String[] args) throws Exception {
ClassPool cPool = new ClassPool(true);
Loader loader = new Loader(cPool);
cPool.appendClassPath(this.ecBaseDir + "/build/rat/apache-rat-0.12.jar");
CtClass ctclzWalker = cPool.get("org.apache.rat.walker.Walker");
CtMethod ctmethodIgnored = ctclzWalker.getDeclaredMethod("ignored");
String ecBaseDirPath = this.ecBaseDir.getPath();
ctmethodIgnored.setBody(""
+ "{"
+ " boolean result = false;"
+ " if (this.filter != null) {"
+ " String name = $1.getName();"
+ " java.io.File dir = $1.getParentFile();"
+ " String relativePath = new java.io.File(\"" + ecBaseDirPath + "\").toURI().relativize($1.toURI()).getPath();"
+ " if (relativePath.endsWith(\"/\")) {"
+ " relativePath = relativePath.substring(0, relativePath.length() - 1);"
+ " }"
+ " result = !this.filter.accept(dir, relativePath);"
+ " }"
+ " return result;"
+ "}"
);
Class clzRatReport = loader.loadClass(this.defaultRatMainClassName);
this.ratReportMainMethod = clzRatReport.getDeclaredMethod("main", String[].class);
}
private void callRat() throws Exception {
String[] ratArgs = this.ratArgList.toArray(new String[this.ratArgList.size()]);
this.ratReportMainMethod.invoke(null, (Object)ratArgs);
}
}
<?xml version='1.0' ?>
<!--
Licensed to the Apache Software Foundation (ASF) under one *
or more contributor license agreements. See the NOTICE file *
distributed with this work for additional information *
regarding copyright ownership. The ASF licenses this file *
to you under the Apache License, Version 2.0 (the *
"License"); you may not use this file except in compliance *
with the License. You may obtain a copy of the License at *
*
http://www.apache.org/licenses/LICENSE-2.0 *
*
Unless required by applicable law or agreed to in writing, *
software distributed under the License is distributed on an *
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
KIND, either express or implied. See the License for the *
specific language governing permissions and limitations *
under the License. *
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method='text'/>
<xsl:template match='/'>
*****************************************************
Summary
-------
Generated at: <xsl:value-of select='rat-report/@timestamp'/>
Notes: <xsl:value-of select='count(descendant::type[attribute::name="notice"])'/>
Binaries: <xsl:value-of select='count(descendant::type[attribute::name="binary"])'/>
Archives: <xsl:value-of select='count(descendant::type[attribute::name="archive"])'/>
Standards: <xsl:value-of select='count(descendant::type[attribute::name="standard"])'/>
Apache Licensed: <xsl:value-of select='count(descendant::header-type[attribute::name="AL "])'/>
Generated Documents: <xsl:value-of select='count(descendant::header-type[attribute::name="GEN "])'/>
JavaDocs are generated, thus a license header is optional.
Generated files do not require license headers.
<xsl:value-of select='count(descendant::header-type[attribute::name="?????"])'/> Unknown Licenses
<xsl:if test="descendant::resource[license-approval/@name='false']">
*****************************************************
Files with unapproved licenses:
<xsl:for-each select='descendant::resource[license-approval/@name="false"]'>
<xsl:text> </xsl:text>
<xsl:value-of select='@name'/>
<xsl:text>
</xsl:text>
</xsl:for-each>
*****************************************************
</xsl:if>
<xsl:if test="descendant::resource[type/@name='archive']">
Archives:
<xsl:for-each select='descendant::resource[type/@name="archive"]'>
+ <xsl:value-of select='@name'/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:if>
*****************************************************
Files with Apache License headers will be marked AL
Binary files (which do not require any license headers) will be marked B
Compressed archives will be marked A
Notices, licenses etc. will be marked N
<xsl:for-each select='descendant::resource'>
<xsl:choose>
<xsl:when test='license-approval/@name="false"'>!</xsl:when>
<xsl:otherwise><xsl:text> </xsl:text></xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test='type/@name="notice"'>N </xsl:when>
<xsl:when test='type/@name="archive"'>A </xsl:when>
<xsl:when test='type/@name="binary"'>B </xsl:when>
<xsl:when test='type/@name="standard"'><xsl:value-of select='header-type/@name'/></xsl:when>
<xsl:otherwise>!!!!!</xsl:otherwise>
</xsl:choose>
<xsl:text> </xsl:text>
<xsl:value-of select='@name'/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
\ No newline at end of file
......@@ -17,7 +17,6 @@
* under the License.
*/
import quantile from './quantile';
import * as numberUtil from '../../src/util/number';
/**
......@@ -58,9 +57,9 @@ export default function (rawData, opt) {
axisData.push(i + '');
var ascList = numberUtil.asc(rawData[i].slice());
var Q1 = quantile(ascList, 0.25);
var Q2 = quantile(ascList, 0.5);
var Q3 = quantile(ascList, 0.75);
var Q1 = numberUtil.quantile(ascList, 0.25);
var Q2 = numberUtil.quantile(ascList, 0.5);
var Q3 = numberUtil.quantile(ascList, 0.75);
var min = ascList[0];
var max = ascList[ascList.length - 1];
......
......@@ -74,7 +74,9 @@ echarts.extendSeriesModel({
coordinateSystem: 'cartesian2d', // Can be set as 'none'
zlevel: 0,
z: 2,
legendHoverLink: true
legendHoverLink: true,
useTransform: true
// Cartesian coordinate system
// xAxisIndex: 0,
......@@ -112,24 +114,27 @@ echarts.extendChartView({
/**
* @override
*/
render: function (customSeries, ecModel, api) {
render: function (customSeries, ecModel, api, payload) {
var oldData = this._data;
var data = customSeries.getData();
var group = this.group;
var renderItem = makeRenderItem(customSeries, data, ecModel, api);
this.group.removeAll();
// By default, merge mode is applied. In most cases, custom series is
// used in the scenario that data amount is not large but graphic elements
// is complicated, where merge mode is probably necessary for optimization.
// For example, reuse graphic elements and only update the transform when
// roam or data zoom according to `actionType`.
data.diff(oldData)
.add(function (newIdx) {
createOrUpdate(
null, newIdx, renderItem(newIdx), customSeries, group, data
null, newIdx, renderItem(newIdx, payload), customSeries, group, data
);
})
.update(function (newIdx, oldIdx) {
var el = oldData.getItemGraphicEl(oldIdx);
createOrUpdate(
el, newIdx, renderItem(newIdx), customSeries, group, data
el, newIdx, renderItem(newIdx, payload), customSeries, group, data
);
})
.remove(function (oldIdx) {
......@@ -146,7 +151,7 @@ echarts.extendChartView({
this._data = null;
},
incrementalRender: function (params, customSeries, ecModel, api) {
incrementalRender: function (params, customSeries, ecModel, api, payload) {
var data = customSeries.getData();
var renderItem = makeRenderItem(customSeries, data, ecModel, api);
function setIncrementalAndHoverLayer(el) {
......@@ -156,7 +161,7 @@ echarts.extendChartView({
}
}
for (var idx = params.start; idx < params.end; idx++) {
var el = createOrUpdate(null, idx, renderItem(idx), customSeries, this.group, data);
var el = createOrUpdate(null, idx, renderItem(idx, payload), customSeries, this.group, data);
el.traverse(setIncrementalAndHoverLayer);
}
},
......@@ -328,13 +333,16 @@ function makeRenderItem(customSeries, data, ecModel, api) {
var currLabelEmphasisModel;
var currVisualColor;
return function (dataIndexInside) {
return function (dataIndexInside, payload) {
currDataIndexInside = dataIndexInside;
currDirty = true;
return renderItem && renderItem(
zrUtil.defaults({
dataIndexInside: dataIndexInside,
dataIndex: data.getRawIndex(dataIndexInside)
dataIndex: data.getRawIndex(dataIndexInside),
// Can be used for optimization when zoom or roam.
actionType: payload ? payload.type : null
}, userParams),
userAPI
) || {};
......@@ -495,13 +503,20 @@ function createOrUpdate(el, dataIndex, elOption, animatableModel, group, data) {
}
function doCreateOrUpdate(el, dataIndex, elOption, animatableModel, group, data) {
elOption = elOption || {};
var elOptionType = elOption.type;
if (el
&& elOptionType !== el.__customGraphicType
&& (elOptionType !== 'path' || elOption.pathData !== el.__customPathData)
&& (elOptionType !== 'image' || elOption.style.image !== el.__customImagePath)
&& (elOptionType !== 'text' || elOption.style.text !== el.__customText)
) {
if (el && (
// Also consider that if `renderItem` returns nothing, the original element
// (if exists) will be removed (elOption is an empty object in that case).
elOptionType == null
|| elOption.$merge === false
|| (elOptionType !== el.__customGraphicType
&& (elOptionType !== 'path' || elOption.pathData !== el.__customPathData)
&& (elOptionType !== 'image' || elOption.style.image !== el.__customImagePath)
&& (elOptionType !== 'text' || elOption.style.text !== el.__customText)
)
)) {
group.remove(el);
el = null;
}
......@@ -515,12 +530,18 @@ function doCreateOrUpdate(el, dataIndex, elOption, animatableModel, group, data)
!el && (el = createEl(elOption));
updateEl(el, dataIndex, elOption, animatableModel, data, isInit);
if (elOptionType === 'group') {
// If `renderItem` returns no children, follow the principle of
// "merge", remain the children of the original elements
// (if exists). The feature can help optimization when roam and
// data zoom. If intending to clear children, `renderItem` could
// returns an empty array as children.
var newChildren = elOption.children;
if (elOptionType === 'group' && newChildren) {
var oldChildren = el.children() || [];
var newChildren = elOption.children || [];
// By default, do not diff elements by name inside a
// group, because that might be lower performance.
if (elOption.diffChildrenByName) {
// lower performance.
diffGroupChildren({
oldChildren: oldChildren,
newChildren: newChildren,
......@@ -530,8 +551,9 @@ function doCreateOrUpdate(el, dataIndex, elOption, animatableModel, group, data)
data: data
});
}
// Mapping children of a group simply by index, which
// might be better performance.
else {
// better performance.
var index = 0;
for (; index < newChildren.length; index++) {
doCreateOrUpdate(
......@@ -549,6 +571,7 @@ function doCreateOrUpdate(el, dataIndex, elOption, animatableModel, group, data)
}
}
// Always add whatever already added to ensure sequence.
group.add(el);
return el;
......
......@@ -132,7 +132,7 @@ export default echarts.extendChartView({
var itemModel = data.getItemModel(idx);
// Update draggable
el.off('drag').off('dragend');
var draggable = data.getItemModel(idx).get('draggable');
var draggable = itemModel.get('draggable');
if (draggable) {
el.on('drag', function () {
if (forceLayout) {
......@@ -321,23 +321,23 @@ export default echarts.extendChartView({
controller
.off('pan')
.off('zoom')
.on('pan', function (dx, dy) {
roamHelper.updateViewOnPan(controllerHost, dx, dy);
.on('pan', function (e) {
roamHelper.updateViewOnPan(controllerHost, e.dx, e.dy);
api.dispatchAction({
seriesId: seriesModel.id,
type: 'graphRoam',
dx: dx,
dy: dy
dx: e.dx,
dy: e.dy
});
})
.on('zoom', function (zoom, mouseX, mouseY) {
roamHelper.updateViewOnZoom(controllerHost, zoom, mouseX, mouseY);
.on('zoom', function (e) {
roamHelper.updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY);
api.dispatchAction({
seriesId: seriesModel.id,
type: 'graphRoam',
zoom: zoom,
originX: mouseX,
originY: mouseY
zoom: e.scale,
originX: e.originX,
originY: e.originY
});
this._updateNodeAndLinkScale();
adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel));
......
......@@ -19,6 +19,7 @@
import * as echarts from '../../echarts';
import {updateCenterAndZoom} from '../../action/roamHelper';
import '../helper/focusNodeAdjacencyAction';
var actionInfo = {
type: 'graphRoam',
......@@ -49,28 +50,3 @@ echarts.registerAction(actionInfo, function (payload, ecModel) {
});
});
/**
* @payload
* @property {number} [seriesIndex]
* @property {string} [seriesId]
* @property {string} [seriesName]
* @property {number} [dataIndex]
*/
echarts.registerAction({
type: 'focusNodeAdjacency',
event: 'focusNodeAdjacency',
update: 'series.graph:focusNodeAdjacency'
}, function () {});
/**
* @payload
* @property {number} [seriesIndex]
* @property {string} [seriesId]
* @property {string} [seriesName]
*/
echarts.registerAction({
type: 'unfocusNodeAdjacency',
event: 'unfocusNodeAdjacency',
update: 'series.graph:unfocusNodeAdjacency'
}, function () {});
......@@ -17,44 +17,29 @@
* under the License.
*/
import * as echarts from '../../echarts';
/**
* Copyright (c) 2010-2015, Michael Bostock
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * The name Michael Bostock may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @payload
* @property {number} [seriesIndex]
* @property {string} [seriesId]
* @property {string} [seriesName]
* @property {number} [dataIndex]
*/
echarts.registerAction({
type: 'focusNodeAdjacency',
event: 'focusNodeAdjacency',
update: 'series:focusNodeAdjacency'
}, function () {});
/**
* @see <https://github.com/mbostock/d3/blob/master/src/arrays/quantile.js>
* @see <http://en.wikipedia.org/wiki/Quantile>
* @param {Array.<number>} ascArr
* @payload
* @property {number} [seriesIndex]
* @property {string} [seriesId]
* @property {string} [seriesName]
*/
export default function(ascArr, p) {
var H = (ascArr.length - 1) * p + 1,
h = Math.floor(H),
v = +ascArr[h - 1],
e = H - h;
return e ? v + e * (ascArr[h] - v) : v;
}
echarts.registerAction({
type: 'unfocusNodeAdjacency',
event: 'unfocusNodeAdjacency',
update: 'series:unfocusNodeAdjacency'
}, function () {});
......@@ -111,6 +111,8 @@ var SankeySeries = SeriesModel.extend({
// control if the node can move or not
draggable: true,
focusNodeAdjacency: false,
// the number of iterations to change the position of the node
layoutIterations: 32,
......
......@@ -24,6 +24,43 @@
import * as graphic from '../../util/graphic';
import * as echarts from '../../echarts';
import * as zrUtil from 'zrender/src/core/util';
var nodeOpacityPath = ['itemStyle', 'opacity'];
var lineOpacityPath = ['lineStyle', 'opacity'];
function getItemOpacity(item, opacityPath) {
return item.getVisual('opacity') || item.getModel().get(opacityPath);
}
function fadeOutItem(item, opacityPath, opacityRatio) {
var el = item.getGraphicEl();
var opacity = getItemOpacity(item, opacityPath);
if (opacityRatio != null) {
opacity == null && (opacity = 1);
opacity *= opacityRatio;
}
el.downplay && el.downplay();
el.traverse(function (child) {
if (child.type !== 'group') {
child.setStyle('opacity', opacity);
}
});
}
function fadeInItem(item, opacityPath) {
var opacity = getItemOpacity(item, opacityPath);
var el = item.getGraphicEl();
el.highlight && el.highlight();
el.traverse(function (child) {
if (child.type !== 'group') {
child.setStyle('opacity', opacity);
}
});
}
var SankeyShape = graphic.extendShape({
shape: {
......@@ -180,10 +217,11 @@ export default echarts.extendChartView({
rect.dataType = 'node';
});
var draggable = seriesModel.get('draggable');
if (draggable) {
nodeData.eachItemGraphicEl(function (el, dataIndex) {
nodeData.eachItemGraphicEl(function (el, dataIndex) {
var itemModel = nodeData.getItemModel(dataIndex);
// var draggable = seriesModel.get('draggable');
if (itemModel.get('draggable')) {
el.drift = function (dx, dy) {
this.shape.x += dx;
this.shape.y += dy;
......@@ -199,9 +237,45 @@ export default echarts.extendChartView({
el.draggable = true;
el.cursor = 'move';
});
}
}
if (itemModel.get('focusNodeAdjacency')) {
el.off('mouseover').on('mouseover', function () {
api.dispatchAction({
type: 'focusNodeAdjacency',
seriesId: seriesModel.id,
dataIndex: el.dataIndex
});
});
el.off('mouseout').on('mouseout', function () {
api.dispatchAction({
type: 'unfocusNodeAdjacency',
seriesId: seriesModel.id
})
});
}
});
edgeData.eachItemGraphicEl(function (el, dataIndex) {
var edgeModel = edgeData.getItemModel(dataIndex);
if (edgeModel.get('focusNodeAdjacency')) {
el.off('mouseover').on('mouseover', function () {
api.dispatchAction({
type: 'focusNodeAdjacency',
seriesId: seriesModel.id,
edgeDataIndex: el.dataIndex
});
});
el.off('mouseout').on('mouseout', function () {
api.dispatchAction({
type: 'unfocusNodeAdjacency',
seriesId: seriesModel.id,
});
})
}
});
if (!this._data && seriesModel.get('animation')) {
group.setClipPath(createGridClipShape(group.getBoundingRect(), seriesModel, function () {
group.removeClipPath();
......@@ -211,7 +285,77 @@ export default echarts.extendChartView({
this._data = seriesModel.getData();
},
dispose: function () {}
dispose: function () {},
focusNodeAdjacency: function (seriesModel, ecModel, api, payload) {
var data = this._model.getData();
var graph = data.graph;
var dataIndex = payload.dataIndex;
var itemModel = data.getItemModel(dataIndex);
var edgeDataIndex = payload.edgeDataIndex;
if (dataIndex == null && edgeDataIndex == null) {
return;
}
var node = graph.getNodeByIndex(dataIndex);
var edge = graph.getEdgeByIndex(edgeDataIndex);
graph.eachNode(function (node) {
fadeOutItem(node, nodeOpacityPath, 0.1);
});
graph.eachEdge(function (edge) {
fadeOutItem(edge, lineOpacityPath, 0.1);
});
if (node) {
fadeInItem(node, nodeOpacityPath);
var focusNodeAdj = itemModel.get('focusNodeAdjacency');
if (focusNodeAdj === 'outEdges') {
zrUtil.each(node.outEdges, function (edge) {
if (edge.dataIndex < 0) {
return;
}
fadeInItem(edge, lineOpacityPath);
fadeInItem(edge.node2, nodeOpacityPath);
});
}
else if (focusNodeAdj === 'inEdges') {
zrUtil.each(node.inEdges, function (edge) {
if (edge.dataIndex < 0) {
return;
}
fadeInItem(edge, lineOpacityPath);
fadeInItem(edge.node1, nodeOpacityPath);
});
}
else if (focusNodeAdj === 'allEdges') {
zrUtil.each(node.edges, function (edge) {
if (edge.dataIndex < 0) {
return;
}
fadeInItem(edge, lineOpacityPath);
fadeInItem(edge.node1, nodeOpacityPath);
fadeInItem(edge.node2, nodeOpacityPath);
});
}
}
if (edge) {
fadeInItem(edge, lineOpacityPath);
fadeInItem(edge.node1, nodeOpacityPath);
fadeInItem(edge.node2, nodeOpacityPath);
}
},
unfocusNodeAdjacency: function (seriesModel, ecModel, api, payload) {
var graph = this._model.getGraph();
graph.eachNode(function (node) {
fadeOutItem(node, nodeOpacityPath);
});
graph.eachEdge(function (edge) {
fadeOutItem(edge, lineOpacityPath);
});
}
});
// add animation to the view
......@@ -232,4 +376,4 @@ function createGridClipShape(rect, seriesModel, cb) {
}, seriesModel, cb);
return rectEl;
}
\ No newline at end of file
}
......@@ -17,7 +17,13 @@
* under the License.
*/
/**
* @file The interactive action of sankey view
* @author Deqing Li(annong035@gmail.com)
*/
import * as echarts from '../../echarts';
import '../helper/focusNodeAdjacencyAction';
echarts.registerAction({
type: 'dragNode',
......
......@@ -19,7 +19,7 @@
/**
* @file Visual encoding for themeRiver view
* @author Deqing Li(annong035@gmail.com)
* @author Deqing Li(annong035@gmail.com)
*/
import {createHashMap} from 'zrender/src/core/util';
......
......@@ -27,4 +27,4 @@ import visualSymbol from '../visual/symbol';
import treeLayout from './tree/treeLayout';
echarts.registerVisual(visualSymbol('tree', 'circle'));
echarts.registerLayout(treeLayout);
echarts.registerLayout(treeLayout);
\ No newline at end of file
......@@ -19,6 +19,7 @@
/**
* @file Create data struct and define tree view's series model
* @author Deqing Li(annong035@gmail.com)
*/
import SeriesModel from '../../model/Series';
......@@ -75,7 +76,7 @@ export default SeriesModel.extend({
return tree.data;
},
/**
* Make the configuration 'orient' backward compatibly, with 'horizontal = LR', 'vertical = TB'.
* @returns {string} orient
......@@ -91,6 +92,14 @@ export default SeriesModel.extend({
return orient;
},
setZoom: function (zoom) {
this.option.zoom = zoom;
},
setCenter: function (center) {
this.option.center = center;
},
/**
* @override
* @param {number} dataIndex
......@@ -123,6 +132,15 @@ export default SeriesModel.extend({
// the layout of the tree, two value can be selected, 'orthogonal' or 'radial'
layout: 'orthogonal',
roam: false,
// Symbol size scale ratio in roam
nodeScaleRatio: 0.4,
// Default on center of graph
center: null,
zoom: 1,
// The orient of orthoginal layout, can be setted to 'LR', 'TB', 'RL', 'BT'.
// and the backward compatibility configuration 'horizontal = LR', 'vertical = TB'.
orient: 'LR',
......
......@@ -19,6 +19,7 @@
/**
* @file This file used to draw tree view
* @author Deqing Li(annong035@gmail.com)
*/
import * as zrUtil from 'zrender/src/core/util';
......@@ -26,6 +27,11 @@ import * as graphic from '../../util/graphic';
import SymbolClz from '../helper/Symbol';
import {radialCoordinate} from './layoutHelper';
import * as echarts from '../../echarts';
import * as bbox from 'zrender/src/core/bbox';
import View from '../../coord/View';
import * as roamHelper from '../../component/helper/roamHelper';
import RoamController from '../../component/helper/RoamController';
import {onIrrelevantElement} from '../../component/helper/cursorHelper';
export default echarts.extendChartView({
......@@ -52,6 +58,9 @@ export default echarts.extendChartView({
this._mainGroup = new graphic.Group();
this.group.add(this._mainGroup);
this._controller = new RoamController(api.getZr());
this._controllerHost = {target: this.group};
},
render: function (seriesModel, ecModel, api, payload) {
......@@ -71,6 +80,9 @@ export default echarts.extendChartView({
group.attr('position', [layoutInfo.x, layoutInfo.y]);
}
this._updateViewCoordSys(seriesModel);
this._updateController(seriesModel, ecModel, api);
var oldData = this._data;
var seriesScope = {
......@@ -114,6 +126,10 @@ export default echarts.extendChartView({
})
.execute();
this._nodeScaleRatio = seriesModel.get('nodeScaleRatio');
this._updateNodeAndLinkScale(seriesModel);
if (seriesScope.expandAndCollapse === true) {
data.eachItemGraphicEl(function (el, dataIndex) {
el.off('click').on('click', function () {
......@@ -129,7 +145,114 @@ export default echarts.extendChartView({
this._data = data;
},
dispose: function () {},
_updateViewCoordSys: function (seriesModel) {
var data = seriesModel.getData();
var points = [];
data.each(function (idx) {
var layout = data.getItemLayout(idx);
if (layout && !isNaN(layout.x) && !isNaN(layout.y)) {
points.push([+layout.x, +layout.y]);
}
});
var min = [];
var max = [];
bbox.fromPoints(points, min, max);
// If width or height is 0
if (max[0] - min[0] === 0) {
max[0] += 1;
min[0] -= 1;
}
if (max[1] - min[1] === 0) {
max[1] += 1;
min[1] -= 1;
}
var viewCoordSys = seriesModel.coordinateSystem = new View();
viewCoordSys.zoomLimit = seriesModel.get('scaleLimit');
viewCoordSys.setBoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]);
viewCoordSys.setCenter(seriesModel.get('center'));
viewCoordSys.setZoom(seriesModel.get('zoom'));
this.group.attr({
position: viewCoordSys.position,
scale: viewCoordSys.scale
});
this._viewCoordSys = viewCoordSys;
},
_updateController: function (seriesModel, ecModel, api) {
var controller = this._controller;
var controllerHost = this._controllerHost;
var group = this.group;
controller.setPointerChecker(function (e, x, y) {
var rect = group.getBoundingRect();
rect.applyTransform(group.transform);
return rect.contain(x, y)
&& !onIrrelevantElement(e, api, seriesModel);
});
controller.enable(seriesModel.get('roam'));
controllerHost.zoomLimit = seriesModel.get('scaleLimit');
controllerHost.zoom = seriesModel.coordinateSystem.getZoom();
controller.off('pan').off('zoom')
.on('pan', function (e) {
roamHelper.updateViewOnPan(controllerHost, e.dx, e.dy);
api.dispatchAction({
seriesId: seriesModel.id,
type: 'treeRoam',
dx: e.dx,
dy: e.dy
});
}, this)
.on('zoom', function (e) {
roamHelper.updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY);
api.dispatchAction({
seriesId: seriesModel.id,
type: 'treeRoam',
zoom: e.scale,
originX: e.originX,
originY: e.originY
});
this._updateNodeAndLinkScale(seriesModel);
}, this);
},
_updateNodeAndLinkScale: function (seriesModel) {
var data = seriesModel.getData();
var nodeScale = this._getNodeGlobalScale(seriesModel);
var invScale = [nodeScale, nodeScale];
data.eachItemGraphicEl(function (el, idx) {
el.attr('scale', invScale);
});
},
_getNodeGlobalScale: function (seriesModel) {
var coordSys = seriesModel.coordinateSystem;
if (coordSys.type !== 'view') {
return 1;
}
var nodeScaleRatio = this._nodeScaleRatio;
var groupScale = coordSys.scale;
var groupZoom = (groupScale && groupScale[0]) || 1;
// Scale node when zoom changes
var roamZoom = coordSys.getZoom();
var nodeScale = (roamZoom - 1) * nodeScaleRatio + 1;
return nodeScale / groupZoom;
},
dispose: function () {
this._controller && this._controller.dispose();
this._controllerHost = {};
},
remove: function () {
this._mainGroup.removeAll();
......@@ -258,7 +381,7 @@ function updateNode(data, dataIndex, symbolEl, group, seriesModel, seriesScope)
if (!edge) {
edge = symbolEl.__edge = new graphic.BezierCurve({
shape: getEdgeShape(seriesScope, sourceOldLayout, sourceOldLayout),
style: zrUtil.defaults({opacity: 0}, seriesScope.lineStyle)
style: zrUtil.defaults({opacity: 0, strokeNoScale: true}, seriesScope.lineStyle)
});
}
......
......@@ -267,7 +267,7 @@ function nextAncestor(nodeInLeft, node, ancestor) {
* @param {module:echarts/data/Tree~TreeNode} wr
* @param {number} shift [description]
*/
function moveSubtree(wl, wr,shift) {
function moveSubtree(wl, wr, shift) {
var change = shift / (wr.hierNode.i - wl.hierNode.i);
wr.hierNode.change -= change;
wr.hierNode.shift += shift;
......
......@@ -18,6 +18,7 @@
*/
import * as echarts from '../../echarts';
import {updateCenterAndZoom} from '../../action/roamHelper';
echarts.registerAction({
type: 'treeExpandAndCollapse',
......@@ -32,3 +33,21 @@ echarts.registerAction({
});
});
echarts.registerAction({
type: 'treeRoam',
event: 'treeRoam',
update: 'none'
}, function (payload, ecModel) {
ecModel.eachComponent({mainType: 'series', query: payload}, function (seriesModel) {
var coordSys = seriesModel.coordinateSystem;
var res = updateCenterAndZoom(coordSys, payload);
seriesModel.setCenter
&& seriesModel.setCenter(res.center);
seriesModel.setZoom
&& seriesModel.setZoom(res.zoom);
});
});
\ No newline at end of file
......@@ -431,9 +431,9 @@ export default echarts.extendChartView({
/**
* @private
*/
_onPan: function (dx, dy) {
_onPan: function (e) {
if (this._state !== 'animating'
&& (Math.abs(dx) > DRAG_THRESHOLD || Math.abs(dy) > DRAG_THRESHOLD)
&& (Math.abs(e.dx) > DRAG_THRESHOLD || Math.abs(e.dy) > DRAG_THRESHOLD)
) {
// These param must not be cached.
var root = this.seriesModel.getData().tree.root;
......@@ -453,7 +453,7 @@ export default echarts.extendChartView({
from: this.uid,
seriesId: this.seriesModel.id,
rootRect: {
x: rootLayout.x + dx, y: rootLayout.y + dy,
x: rootLayout.x + e.dx, y: rootLayout.y + e.dy,
width: rootLayout.width, height: rootLayout.height
}
});
......@@ -463,7 +463,10 @@ export default echarts.extendChartView({
/**
* @private
*/
_onZoom: function (scale, mouseX, mouseY) {
_onZoom: function (e) {
var mouseX = e.originX;
var mouseY = e.originY;
if (this._state !== 'animating') {
// These param must not be cached.
var root = this.seriesModel.getData().tree.root;
......@@ -490,7 +493,7 @@ export default echarts.extendChartView({
// Scale root bounding rect.
var m = matrix.create();
matrix.translate(m, m, [-mouseX, -mouseY]);
matrix.scale(m, m, [scale, scale]);
matrix.scale(m, m, [e.scale, e.scale]);
matrix.translate(m, m, [mouseX, mouseY]);
rect.applyTransform(m);
......
/*
* 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.
*/
/**
* AxisPointer Interface:
*
......
......@@ -31,6 +31,7 @@ export default DataZoomModel.extend({
zoomLock: false, // Whether disable zoom but only pan.
zoomOnMouseWheel: true, // Can be: true / false / 'shift' / 'ctrl' / 'alt'.
moveOnMouseMove: true, // Can be: true / false / 'shift' / 'ctrl' / 'alt'.
moveOnMouseWheel: false, // Can be: true / false / 'shift' / 'ctrl' / 'alt'.
preventDefaultMouseMove: true
}
});
\ No newline at end of file
......@@ -61,7 +61,11 @@ var InsideZoomView = DataZoomView.extend({
zrUtil.each(coordInfoList, function (coordInfo) {
var coordModel = coordInfo.model;
var dataZoomOption = dataZoomModel.option;
var getRange = {};
zrUtil.each(['pan', 'zoom', 'scrollMove'], function (eventName) {
getRange[eventName] = bind(roamHandlers[eventName], this, coordInfo, coordSysName);
}, this);
roams.register(
api,
......@@ -72,16 +76,8 @@ var InsideZoomView = DataZoomView.extend({
return coordModel.coordinateSystem.containPoint([x, y]);
},
dataZoomId: dataZoomModel.id,
throttleRate: dataZoomModel.get('throttle', true),
panGetRange: bind(this._onPan, this, coordInfo, coordSysName),
zoomGetRange: bind(this._onZoom, this, coordInfo, coordSysName),
zoomLock: dataZoomOption.zoomLock,
disabled: dataZoomOption.disabled,
roamControllerOpt: {
zoomOnMouseWheel: dataZoomOption.zoomOnMouseWheel,
moveOnMouseMove: dataZoomOption.moveOnMouseMove,
preventDefaultMouseMove: dataZoomOption.preventDefaultMouseMove
}
dataZoomModel: dataZoomModel,
getRange: getRange
}
);
}, this);
......@@ -96,12 +92,16 @@ var InsideZoomView = DataZoomView.extend({
roams.unregister(this.api, this.dataZoomModel.id);
InsideZoomView.superApply(this, 'dispose', arguments);
this._range = null;
},
}
});
var roamHandlers = {
/**
* @private
* @this {module:echarts/component/dataZoom/InsideZoomView}
*/
_onPan: function (coordInfo, coordSysName, controller, dx, dy, oldX, oldY, newX, newY) {
zoom: function (coordInfo, coordSysName, controller, e) {
var lastRange = this._range;
var range = lastRange.slice();
......@@ -112,14 +112,22 @@ var InsideZoomView = DataZoomView.extend({
}
var directionInfo = getDirectionInfo[coordSysName](
[oldX, oldY], [newX, newY], axisModel, controller, coordInfo
null, [e.originX, e.originY], axisModel, controller, coordInfo
);
var percentPoint = (
directionInfo.signal > 0
? (directionInfo.pixelStart + directionInfo.pixelLength - directionInfo.pixel)
: (directionInfo.pixel - directionInfo.pixelStart)
) / directionInfo.pixelLength * (range[1] - range[0]) + range[0];
var percentDelta = directionInfo.signal
* (range[1] - range[0])
* directionInfo.pixel / directionInfo.pixelLength;
var scale = Math.max(1 / e.scale, 0);
range[0] = (range[0] - percentPoint) * scale + percentPoint;
range[1] = (range[1] - percentPoint) * scale + percentPoint;
sliderMove(percentDelta, range, [0, 100], 'all');
// Restrict range.
var minMaxSpan = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();
sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan);
this._range = range;
......@@ -129,9 +137,28 @@ var InsideZoomView = DataZoomView.extend({
},
/**
* @private
* @this {module:echarts/component/dataZoom/InsideZoomView}
*/
_onZoom: function (coordInfo, coordSysName, controller, scale, mouseX, mouseY) {
pan: makeMover(function (range, axisModel, coordInfo, coordSysName, controller, e) {
var directionInfo = getDirectionInfo[coordSysName](
[e.oldX, e.oldY], [e.newX, e.newY], axisModel, controller, coordInfo
);
return directionInfo.signal
* (range[1] - range[0])
* directionInfo.pixel / directionInfo.pixelLength;
}),
/**
* @this {module:echarts/component/dataZoom/InsideZoomView}
*/
scrollMove: makeMover(function (range, axisModel, coordInfo, coordSysName, controller, e) {
return (range[1] - range[0]) * e.scrollDelta;
})
};
function makeMover(getPercentDelta) {
return function (coordInfo, coordSysName, controller, e) {
var lastRange = this._range;
var range = lastRange.slice();
......@@ -141,32 +168,19 @@ var InsideZoomView = DataZoomView.extend({
return;
}
var directionInfo = getDirectionInfo[coordSysName](
null, [mouseX, mouseY], axisModel, controller, coordInfo
var percentDelta = getPercentDelta(
range, axisModel, coordInfo, coordSysName, controller, e
);
var percentPoint = (
directionInfo.signal > 0
? (directionInfo.pixelStart + directionInfo.pixelLength - directionInfo.pixel)
: (directionInfo.pixel - directionInfo.pixelStart)
) / directionInfo.pixelLength * (range[1] - range[0]) + range[0];
scale = Math.max(1 / scale, 0);
range[0] = (range[0] - percentPoint) * scale + percentPoint;
range[1] = (range[1] - percentPoint) * scale + percentPoint;
// Restrict range.
var minMaxSpan = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();
sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan);
sliderMove(percentDelta, range, [0, 100], 'all');
this._range = range;
if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) {
return range;
}
}
});
};
}
var getDirectionInfo = {
......
......@@ -27,7 +27,6 @@ import * as zrUtil from 'zrender/src/core/util';
import RoamController from '../../component/helper/RoamController';
import * as throttleUtil from '../../util/throttle';
var curry = zrUtil.curry;
var ATTR = '\0_ec_dataZoom_roams';
......@@ -40,11 +39,11 @@ var ATTR = '\0_ec_dataZoom_roams';
* @param {Function} dataZoomInfo.containsPoint
* @param {Array.<string>} dataZoomInfo.allCoordIds
* @param {string} dataZoomInfo.dataZoomId
* @param {number} dataZoomInfo.throttleRate
* @param {Function} dataZoomInfo.panGetRange
* @param {Function} dataZoomInfo.zoomGetRange
* @param {boolean} [dataZoomInfo.zoomLock]
* @param {boolean} [dataZoomInfo.disabled]
* @param {Object} dataZoomInfo.getRange
* @param {Function} dataZoomInfo.getRange.pan
* @param {Function} dataZoomInfo.getRange.zoom
* @param {Function} dataZoomInfo.getRange.scrollMove
* @param {boolean} dataZoomInfo.dataZoomModel
*/
export function register(api, dataZoomInfo) {
var store = giveStore(api);
......@@ -91,7 +90,7 @@ export function register(api, dataZoomInfo) {
throttleUtil.createOrUpdate(
record,
'dispatchAction',
dataZoomInfo.throttleRate,
dataZoomInfo.dataZoomModel.get('throttle', true),
'fixRate'
);
}
......@@ -136,8 +135,29 @@ function giveStore(api) {
function createController(api, newRecord) {
var controller = new RoamController(api.getZr());
controller.on('pan', curry(onPan, newRecord));
controller.on('zoom', curry(onZoom, newRecord));
zrUtil.each(['pan', 'zoom', 'scrollMove'], function (eventName) {
controller.on(eventName, function (event) {
var batch = [];
zrUtil.each(newRecord.dataZoomInfos, function (info) {
if (!event.isAvailableBehavior(info.dataZoomModel.option)) {
return;
}
var method = (info.getRange || {})[eventName];
var range = method && method(newRecord.controller, event);
!info.dataZoomModel.get('disabled', true) && range && batch.push({
dataZoomId: info.dataZoomId,
start: range[0],
end: range[1]
});
});
batch.length && newRecord.dispatchAction(batch);
});
});
return controller;
}
......@@ -151,33 +171,6 @@ function cleanStore(store) {
});
}
function onPan(record, dx, dy, oldX, oldY, newX, newY) {
wrapAndDispatch(record, function (info) {
return info.panGetRange(record.controller, dx, dy, oldX, oldY, newX, newY);
});
}
function onZoom(record, scale, mouseX, mouseY) {
wrapAndDispatch(record, function (info) {
return info.zoomGetRange(record.controller, scale, mouseX, mouseY);
});
}
function wrapAndDispatch(record, getRange) {
var batch = [];
zrUtil.each(record.dataZoomInfos, function (info) {
var range = getRange(info);
!info.disabled && range && batch.push({
dataZoomId: info.dataZoomId,
start: range[0],
end: range[1]
});
});
batch.length && record.dispatchAction(batch);
}
/**
* This action will be throttled.
*/
......@@ -193,7 +186,6 @@ function dispatchAction(api, batch) {
*/
function mergeControllerParams(dataZoomInfos) {
var controlType;
var opt = {};
// DO NOT use reserved word (true, false, undefined) as key literally. Even if encapsulated
// as string, it is probably revert to reserved word by compress tool. See #7411.
var prefix = 'type_';
......@@ -203,17 +195,30 @@ function mergeControllerParams(dataZoomInfos) {
'type_false': 0,
'type_undefined': -1
};
var preventDefaultMouseMove = true;
zrUtil.each(dataZoomInfos, function (dataZoomInfo) {
var oneType = dataZoomInfo.disabled ? false : dataZoomInfo.zoomLock ? 'move' : true;
var dataZoomModel = dataZoomInfo.dataZoomModel;
var oneType = dataZoomModel.get('disabled', true)
? false
: dataZoomModel.get('zoomLock', true)
? 'move'
: true;
if (typePriority[prefix + oneType] > typePriority[prefix + controlType]) {
controlType = oneType;
}
// Do not support that different 'shift'/'ctrl'/'alt' setting used in one coord sys.
zrUtil.extend(opt, dataZoomInfo.roamControllerOpt);
// Prevent default move event by default. If one false, do not prevent. Otherwise
// users may be confused why it does not work when multiple insideZooms exist.
preventDefaultMouseMove &= dataZoomModel.get('preventDefaultMouseMove', true);
});
return {
controlType: controlType,
opt: opt
opt: {
zoomOnMouseWheel: true,
moveOnMouseMove: true,
moveOnMouseWheel: true,
preventDefaultMouseMove: !!preventDefaultMouseMove
}
};
}
......@@ -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');
});
......@@ -96,6 +98,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;
......@@ -343,32 +389,31 @@ MapDraw.prototype = {
return action;
}
controller.off('pan').on('pan', function (dx, dy) {
controller.off('pan').on('pan', function (e) {
this._mouseDownFlag = false;
roamHelper.updateViewOnPan(controllerHost, dx, dy);
roamHelper.updateViewOnPan(controllerHost, e.dx, e.dy);
api.dispatchAction(zrUtil.extend(makeActionBase(), {
dx: dx,
dy: dy
dx: e.dx,
dy: e.dy
}));
}, this);
controller.off('zoom').on('zoom', function (zoom, mouseX, mouseY) {
controller.off('zoom').on('zoom', function (e) {
this._mouseDownFlag = false;
roamHelper.updateViewOnZoom(controllerHost, zoom, mouseX, mouseY);
roamHelper.updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY);
api.dispatchAction(zrUtil.extend(makeActionBase(), {
zoom: zoom,
originX: mouseX,
originY: mouseY
zoom: e.scale,
originX: e.originX,
originY: e.originY
}));
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]]);
}
......
......@@ -74,8 +74,9 @@ function RoamController(zr) {
* which can be null/undefined or true/false
* or 'pan/move' or 'zoom'/'scale'
* @param {Object} [opt]
* @param {Object} [opt.zoomOnMouseWheel=true]
* @param {Object} [opt.moveOnMouseMove=true]
* @param {Object} [opt.zoomOnMouseWheel=true] The value can be: true / false / 'shift' / 'ctrl' / 'alt'.
* @param {Object} [opt.moveOnMouseMove=true] The value can be: true / false / 'shift' / 'ctrl' / 'alt'.
* @param {Object} [opt.moveOnMouseWheel=false] The value can be: true / false / 'shift' / 'ctrl' / 'alt'.
* @param {Object} [opt.preventDefaultMouseMove=true] When pan.
*/
this.enable = function (controlType, opt) {
......@@ -86,6 +87,8 @@ function RoamController(zr) {
this._opt = zrUtil.defaults(zrUtil.clone(opt) || {}, {
zoomOnMouseWheel: true,
moveOnMouseMove: true,
// By default, wheel do not trigger move.
moveOnMouseWheel: false,
preventDefaultMouseMove: true
});
......@@ -147,7 +150,7 @@ function mousedown(e) {
function mousemove(e) {
if (eventTool.notLeftMouse(e)
|| !checkKeyBinding(this, 'moveOnMouseMove', e)
|| !isAvailableBehavior('moveOnMouseMove', e, this._opt)
|| !this._dragging
|| e.gestureEvent === 'pinch'
|| interactionMutex.isTaken(this._zr, 'globalPan')
......@@ -169,7 +172,9 @@ function mousemove(e) {
this._opt.preventDefaultMouseMove && eventTool.stop(e.event);
this.trigger('pan', dx, dy, oldX, oldY, x, y);
trigger(this, 'pan', 'moveOnMouseMove', e, {
dx: dx, dy: dy, oldX: oldX, oldY: oldY, newX: x, newY: y
});
}
function mouseup(e) {
......@@ -179,41 +184,87 @@ function mouseup(e) {
}
function mousewheel(e) {
var shouldZoom = isAvailableBehavior('zoomOnMouseWheel', e, this._opt);
var shouldMove = isAvailableBehavior('moveOnMouseWheel', e, this._opt);
var wheelDelta = e.wheelDelta;
var absWheelDeltaDelta = Math.abs(wheelDelta);
var originX = e.offsetX;
var originY = e.offsetY;
// wheelDelta maybe -0 in chrome mac.
if (!checkKeyBinding(this, 'zoomOnMouseWheel', e) || e.wheelDelta === 0) {
if (wheelDelta === 0 || (!shouldZoom && !shouldMove)) {
return;
}
// Convenience:
// Mac and VM Windows on Mac: scroll up: zoom out.
// Windows: scroll up: zoom in.
var zoomDelta = e.wheelDelta > 0 ? 1.1 : 1 / 1.1;
zoom.call(this, e, zoomDelta, e.offsetX, e.offsetY);
// console.log(wheelDelta);
if (shouldZoom) {
// Convenience:
// Mac and VM Windows on Mac: scroll up: zoom out.
// Windows: scroll up: zoom in.
// FIXME: Should do more test in different environment.
// wheelDelta is too complicated in difference nvironment
// (https://developer.mozilla.org/en-US/docs/Web/Events/mousewheel),
// although it has been normallized by zrender.
// wheelDelta of mouse wheel is bigger than touch pad.
var factor = absWheelDeltaDelta > 3 ? 1.4 : absWheelDeltaDelta > 1 ? 1.2 : 1.1;
var scale = wheelDelta > 0 ? factor : 1 / factor;
checkPointerAndTrigger(this, 'zoom', 'zoomOnMouseWheel', e, {
scale: scale, originX: originX, originY: originY
});
}
// console.log(shouldMove);
if (shouldMove) {
// FIXME: Should do more test in different environment.
var absDelta = Math.abs(wheelDelta);
// wheelDelta of mouse wheel is bigger than touch pad.
var scrollDelta = (wheelDelta > 0 ? 1 : -1) * (absDelta > 3 ? 0.4 : absDelta > 1 ? 0.15 : 0.05);
checkPointerAndTrigger(this, 'scrollMove', 'moveOnMouseWheel', e, {
scrollDelta: scrollDelta, originX: originX, originY: originY
});
}
}
function pinch(e) {
if (interactionMutex.isTaken(this._zr, 'globalPan')) {
return;
}
var zoomDelta = e.pinchScale > 1 ? 1.1 : 1 / 1.1;
zoom.call(this, e, zoomDelta, e.pinchX, e.pinchY);
var scale = e.pinchScale > 1 ? 1.1 : 1 / 1.1;
checkPointerAndTrigger(this, 'zoom', null, e, {
scale: scale, originX: e.pinchX, originY: e.pinchY
});
}
function zoom(e, zoomDelta, zoomX, zoomY) {
if (this.pointerChecker && this.pointerChecker(e, zoomX, zoomY)) {
function checkPointerAndTrigger(controller, eventName, behaviorToCheck, e, contollerEvent) {
if (controller.pointerChecker
&& controller.pointerChecker(e, contollerEvent.originX, contollerEvent.originY)
) {
// When mouse is out of roamController rect,
// default befavoius should not be be disabled, otherwise
// page sliding is disabled, contrary to expectation.
eventTool.stop(e.event);
this.trigger('zoom', zoomDelta, zoomX, zoomY);
trigger(controller, eventName, behaviorToCheck, e, contollerEvent);
}
}
function checkKeyBinding(roamController, prop, e) {
var setting = roamController._opt[prop];
return setting
&& (!zrUtil.isString(setting) || e.event[setting + 'Key']);
function trigger(controller, eventName, behaviorToCheck, e, contollerEvent) {
// Also provide behavior checker for event listener, for some case that
// multiple components share one listener.
contollerEvent.isAvailableBehavior = zrUtil.bind(isAvailableBehavior, null, behaviorToCheck, e);
controller.trigger(eventName, contollerEvent);
}
// settings: {
// zoomOnMouseWheel
// moveOnMouseMove
// moveOnMouseWheel
// }
// The value can be: true / false / 'shift' / 'ctrl' / 'alt'.
function isAvailableBehavior(behaviorToCheck, e, settings) {
var setting = settings[behaviorToCheck];
return !behaviorToCheck || (
setting && (!zrUtil.isString(setting) || e.event[setting + 'Key'])
);
}
export default RoamController;
\ No newline at end of file
......@@ -217,6 +217,13 @@ TooltipContent.prototype = {
el.style.display = el.innerHTML ? 'block' : 'none';
// If mouse occsionally move over the tooltip, a mouseout event will be
// triggered by canvas, and cuase some unexpectable result like dragging
// stop, "unfocusAdjacency". Here `pointer-events: none` is used to solve
// it. Although it is not suppored by IE8~IE10, fortunately it is a rare
// scenario.
el.style.pointerEvents = this._enterable ? 'auto' : 'none';
this._show = true;
},
......
/*
* 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.
*/
/**
* Coordinate System Interface:
*
......
......@@ -290,6 +290,9 @@ export function makeLabelFormatter(axis) {
if (typeof labelFormatter === 'string') {
labelFormatter = (function (tpl) {
return function (val) {
// For category axis, get raw value; for numeric axis,
// get foramtted label like '1,333,444'.
val = axis.scale.getLabel(val);
return tpl.replace('{value}', val != null ? val : '');
};
})(labelFormatter);
......
......@@ -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')
......
......@@ -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
......
......@@ -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;
}
};
......
......@@ -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
......@@ -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
......@@ -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 {
......
......@@ -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
......@@ -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;
}
};
......
/*
* 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;
}
/*
* 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
};
}
/*
* 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;
}
/*
* 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);
}
};
......@@ -41,7 +41,8 @@ export default function (coordSys) {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height
height: rect.height,
zoom: coordSys.getZoom()
},
api: {
coord: function (data) {
......
......@@ -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);
......
......@@ -201,8 +201,8 @@ var scaleLevels = [
['month', ONE_DAY * 31], // 1M
['week', ONE_DAY * 42], // 6w
['month', ONE_DAY * 62], // 2M
['week', ONE_DAY * 42], // 10w
['quarter', ONE_DAY * 380 / 4], // 3M
['week', ONE_DAY * 70], // 10w
['quarter', ONE_DAY * 95], // 3M
['month', ONE_DAY * 31 * 4], // 4M
['month', ONE_DAY * 31 * 5], // 5M
['half-year', ONE_DAY * 380 / 2], // 6M
......
......@@ -409,6 +409,50 @@ export function nice(val, round) {
return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val;
}
/**
* BSD 3-Clause
*
* Copyright (c) 2010-2015, Michael Bostock
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * The name Michael Bostock may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @see <https://github.com/mbostock/d3/blob/master/src/arrays/quantile.js>
* @see <http://en.wikipedia.org/wiki/Quantile>
* @param {Array.<number>} ascArr
*/
export function quantile(ascArr, p) {
var H = (ascArr.length - 1) * p + 1;
var h = Math.floor(H);
var v = +ascArr[h - 1];
var e = H - h;
return e ? v + e * (ascArr[h] - v) : v;
}
/**
* Order intervals asc, and split them when overlap.
* expect(numberUtil.reformIntervals([
......
......@@ -118,6 +118,7 @@ Chart.prototype = {
/**
* Render in progressive mode.
* @param {Object} params See taskParams in `stream/task.js`
* @param {module:echarts/model/Series} seriesModel
* @param {module:echarts/model/Global} ecModel
* @param {module:echarts/ExtensionAPI} api
......
......@@ -46,6 +46,8 @@ under the License.
}
</style>
<div class="chart" id="mainA"></div>
<h1>[ Test minInterval ]&nbsp;&nbsp;&nbsp; yAxis: {minInterval: 1, min: 0}, series.data: [1]</h1>
<div class="chart" id="main0"></div>
<h1>[ Test category axis interval ]&nbsp;&nbsp;&nbsp; interval of xAxis should be approperiate after rotated.</h1>
......@@ -54,6 +56,75 @@ under the License.
<div class="chart" id="main2"></div>
<script>
require([
'echarts'
], function (echarts) {
var dataCount = 200;
var data = [];
var d = +new Date(2015, 3, 13);
var oneDay = 3600 * 1000;
for (var i = 0; i < dataCount; i++) {
data.push([d, Math.random()]);
d += 2.5 * oneDay;
}
var startValue = '2015-04-24T04:21';
var endValue = '2015-04-24T10:12';
var option = {
tooltip: {
trigger: 'axis'
},
dataZoom: [{
startValue: startValue,
endValue: endValue
}, {
type: 'inside',
startValue: startValue,
endValue: endValue
}],
grid: {
left: '3%',
right: '4%',
bottom: 60,
containLabel: true
},
xAxis: {
type: 'time',
splitNumber: 7
},
yAxis: {},
series: [
{
type:'line',
data: data
}
]
};
testHelper.create(echarts, 'mainA', {
title: 'xAxis (type: time) should be 1 hour interval',
option: option
});
});
</script>
<script>
require([
......
......@@ -40,6 +40,7 @@ under the License.
</style>
<div class="chart" id="main1"></div>
<div class="chart" id="main2"></div>
<div class="chart" id="main3"></div>
......@@ -57,7 +58,11 @@ under the License.
// 'echarts/component/toolbox',
// 'echarts/component/visualMap'
], function (echarts) {
var chart = echarts.init(document.getElementById('main1'));
var el = document.getElementById('main1');
if (!el) {
return;
}
var chart = echarts.init(el);
var DECIMALS = [1];
for (var i = 1; i < 15; i++) {
......@@ -181,7 +186,11 @@ under the License.
require([
'echarts'
], function (echarts) {
var chart = echarts.init(document.getElementById('main2'));
var el = document.getElementById('main2');
if (!el) {
return;
}
var chart = echarts.init(el);
var rotate = 30;
option = {
......@@ -235,5 +244,63 @@ under the License.
</script>
<script>
require([
'echarts'
], function (echarts) {
var chart = echarts.init(document.getElementById('main3'));
var rotate = 30;
option = {
backgroundColor: '#eee',
title: {
text: 'axisLable.formatter: {value} should be correct'
},
grid: {
containLabel: true,
left: 0
},
tooltip: {
trigger: 'axis'
},
xAxis: {
data: ['categoryA', 'categoryB', 'categoryC', 'categoryD', 'categoryE'],
axisLabel: {
formatter: '{value}'
}
},
yAxis: {
axisLabel: {
formatter: '{value}',
rotate: rotate
}
},
series: [{
type: 'line',
data: [
1111111111111,
2222222222222,
3333333333333,
4444444444444,
5555555555555,
6666666666666
]
}]
};
chart.setOption(option);
});
</script>
</body>
</html>
\ No newline at end of file
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="./lib/esl.js"></script>
<script src="./lib/config.js"></script>
<script src="./lib/facePrint.js"></script>
<script src="lib/testHelper.js"></script>
<link rel="stylesheet" href="lib/reset.css" />
</head>
<body>
<style>
h1 {
line-height: 60px;
height: 60px;
background: #ddd;
text-align: center;
font-weight: bold;
font-size: 14px;
}
.chart {
height: 500px;
}
</style>
<div class="chart" id="d3"></div>
<script>
require([
'data/rainfall.json',
'echarts'
], function (data, echarts) {
var option = {
tooltip: {
trigger: 'axis'
},
grid: [
{
show: true,
borderWidth: 0
}
],
xAxis: [
{
type: 'category',
boundaryGap: true,
axisLabel: {show: true},
splitLine: {show: false},
data: data.category,
gridIndex: 0
}
],
yAxis: [
{
boundaryGap: false,
axisLabel: {
},
axisLine: {
lineStyle: {
color: '#666'
}
},
gridIndex: 0
}
],
series: [
{
name: '降水量',
type: 'line',
data: data.rainfall,
itemStyle: {
normal: {
areaStyle: {}
}
},
xAxisIndex: 0,
yAxisIndex: 0,
}
],
dataZoom: [
{
type: 'inside',
zoomOnMouseWheel: false,
xAxisIndex: 0,
// zoomLock: true,
start: 30,
end: 40
},
{
type: 'inside',
yAxisIndex: 0,
zoomOnMouseWheel: false,
moveOnMouseWheel: true,
// zoomLock: true,
start: 30,
end: 40
},
{
type: 'slider',
start: 30,
end: 40
},
{
type: 'slider',
yAxisIndex: 0
}
]
};
testHelper.create(echarts, 'd3', {
title: 'Simulate browser scroll bar on y scroll',
option: option,
height: 600
});
})
</script>
</body>
</html>
\ No newline at end of file
......@@ -33,6 +33,7 @@ under the License.
margin: 0;
}
</style>
新疆 should be yellow
<div id="main"></div>
<script>
......@@ -279,7 +280,7 @@ under the License.
geo: [{
map: 'china',
roam: true,
left: 10,
left: 100,
width: 300,
// zoom: 1.5,
scaleLimit: {
......@@ -290,12 +291,14 @@ under the License.
name: '新疆',
label: {
normal: {
show: true
formatter: 'asdf',
show: true,
offset: [100, 200]
}
},
itemStyle: {
normal: {
areaColor: '#aaa'
areaColor: 'yellow'
}
}
}]
......@@ -304,7 +307,7 @@ under the License.
roam: true,
selectedMode: 'single',
left: null,
right: 10,
right: 100,
width: 300
}],
tooltip: {
......
......@@ -36,175 +36,169 @@ under the License.
</style>
<div id="main"><div>
<script>
require([
'echarts'
// 'echarts/chart/sankey',
// 'echarts/component/tooltip'
], function (echarts) {
require(['echarts'], function (echarts) {
var chart = echarts.init(document.getElementById('main'), null, {
var chart = echarts.init(document.getElementById('main'), null, {});
});
window.onresize = function () {
chart.resize();
};
window.onresize = function () {
chart.resize();
};
// var data = {
// nodes: [
// {name: 'Brazil'},
// {name: 'Canada'},
// {name: 'Mexico'},
// {name: 'USA'},
// {name: 'Portugal'},
// {name: 'France'},
// {name: 'Spain'},
// {name: 'England'},
// {name: 'Angola'},
// {name: 'Senegal'},
// {name: 'Morocco'},
// {name: 'South Africa'},
// {name: 'Mali'},
// {name: 'China'},
// {name: 'India'},
// {name: 'Japan'}
// ],
// links: [
// {source: 'Brazil', target: 'Portugal', value: 5},
// {source: 'Brazil', target: 'France', value: 1},
// {source: 'Brazil', target: 'Spain', value: 1},
// {source: 'Brazil', target: 'England', value: 1},
// {source: 'Canada', target: 'Portugal', value: 1},
// {source: 'Canada', target: 'France', value: 5},
// {source: 'Canada', target: 'England', value: 1},
// {source: 'Mexico', target: 'Portugal', value: 1},
// {source: 'Mexico', target: 'France', value: 1},
// {source: 'Mexico', target: 'Spain', value: 5},
// {source: 'Mexico', target: 'England', value: 1},
// {source: 'USA', target: 'Portugal', value: 1},
// {source: 'USA', target: 'France', value: 1},
// {source: 'USA', target: 'Spain', value: 1},
// {source: 'USA', target: 'England', value: 5},
// {source: 'Portugal', target: 'Angola', value: 2},
// {source: 'Portugal', target: 'Senegal', value: 1},
// {source:'Portugal', target: 'Morocco', value: 1},
// {source: 'Portugal', target: 'South Africa', value: 3},
// {source: 'France', target: 'Angola', value: 1},
// {source: 'France', target: 'Senegal', value: 3},
// {source: 'France', target: 'Mali', value: 3},
// {source: 'France', target: 'Morocco', value: 3},
// {source: 'France', target: 'South Africa', value: 1},
// {source: 'Spain', target: 'Senegal', value: 1},
// {source: 'Spain', target: 'Morocco', value: 3},
// {source: 'Spain', target: 'South Africa', value: 1},
// {source: 'England', target: 'Angola', value: 1},
// {source: 'England', target: 'Senegal', value: 1},
// {source: 'England', target: 'Morocco', value: 2},
// {source: 'England', target: 'South Africa', value: 7},
// {source: 'South Africa', target: 'China', value: 5},
// {source: 'South Africa', target: 'India', value: 1},
// {source: 'South Africa', target: 'Japan', value: 3},
// {source: 'Angola', target: 'China', value: 5},
// {source: 'Angola', target: 'India', value: 1},
// {source: 'Angola', target: 'Japan', value: 3},
// {source: 'Senegal', target: 'China', value: 5},
// {source: 'Senegal', target: 'India', value: 1},
// {source: 'Senegal', target: 'Japan', value: 3},
// {source: 'Mali', target: 'China', value: 5},
// {source: 'Mali', target: 'India', value: 1},
// {source: 'Mali', target: 'Japan', value: 3},
// {source: 'Morocco', target: 'China', value: 5},
// {source: 'Morocco', target: 'India', value: 1},
// {source: 'Morocco', target: 'Japan', value: 3}
// ]
// };
// chart.on('click', function (params) {
// console.log(params, params.data);
// });
// var data = {
// nodes: [
// {name: 'Brazil'},
// {name: 'Canada'},
// {name: 'Mexico'},
// {name: 'USA'},
// {name: 'Portugal'},
// {name: 'France'},
// {name: 'Spain'},
// {name: 'England'},
// {name: 'Angola'},
// {name: 'Senegal'},
// {name: 'Morocco'},
// {name: 'South Africa'},
// {name: 'Mali'},
// {name: 'China'},
// {name: 'India'},
// {name: 'Japan'}
// ],
// links: [
// {source: 'Brazil', target: 'Portugal', value: 5},
// {source: 'Brazil', target: 'France', value: 1},
// {source: 'Brazil', target: 'Spain', value: 1},
// {source: 'Brazil', target: 'England', value: 1},
// {source: 'Canada', target: 'Portugal', value: 1},
// {source: 'Canada', target: 'France', value: 5},
// {source: 'Canada', target: 'England', value: 1},
// {source: 'Mexico', target: 'Portugal', value: 1},
// {source: 'Mexico', target: 'France', value: 1},
// {source: 'Mexico', target: 'Spain', value: 5},
// {source: 'Mexico', target: 'England', value: 1},
// {source: 'USA', target: 'Portugal', value: 1},
// {source: 'USA', target: 'France', value: 1},
// {source: 'USA', target: 'Spain', value: 1},
// {source: 'USA', target: 'England', value: 5},
// {source: 'Portugal', target: 'Angola', value: 2},
// {source: 'Portugal', target: 'Senegal', value: 1},
// {source:'Portugal', target: 'Morocco', value: 1},
// {source: 'Portugal', target: 'South Africa', value: 3},
// {source: 'France', target: 'Angola', value: 1},
// {source: 'France', target: 'Senegal', value: 3},
// {source: 'France', target: 'Mali', value: 3},
// {source: 'France', target: 'Morocco', value: 3},
// {source: 'France', target: 'South Africa', value: 1},
// {source: 'Spain', target: 'Senegal', value: 1},
// {source: 'Spain', target: 'Morocco', value: 3},
// {source: 'Spain', target: 'South Africa', value: 1},
// {source: 'England', target: 'Angola', value: 1},
// {source: 'England', target: 'Senegal', value: 1},
// {source: 'England', target: 'Morocco', value: 2},
// {source: 'England', target: 'South Africa', value: 7},
// {source: 'South Africa', target: 'China', value: 5},
// {source: 'South Africa', target: 'India', value: 1},
// {source: 'South Africa', target: 'Japan', value: 3},
// {source: 'Angola', target: 'China', value: 5},
// {source: 'Angola', target: 'India', value: 1},
// {source: 'Angola', target: 'Japan', value: 3},
// {source: 'Senegal', target: 'China', value: 5},
// {source: 'Senegal', target: 'India', value: 1},
// {source: 'Senegal', target: 'Japan', value: 3},
// {source: 'Mali', target: 'China', value: 5},
// {source: 'Mali', target: 'India', value: 1},
// {source: 'Mali', target: 'Japan', value: 3},
// {source: 'Morocco', target: 'China', value: 5},
// {source: 'Morocco', target: 'India', value: 1},
// {source: 'Morocco', target: 'Japan', value: 3}
// ]
// };
var testData = {
nodes: [
{
name: 'a'
},
{
name: 'b'
},
{
name: 'a1'
},
{
name: 'b1'
},
{
name: 'c'
},
{
name: 'e'
}
],
links: [
{
source: 'a',
target: 'a1',
value: 5
},
{
source: 'e',
target: 'b',
value: 3
},
{
source: 'a',
target: 'b1',
value: 3
},
{
source: 'b1',
target: 'a1',
value: 1
},
{
source: 'b1',
target: 'c',
value: 2
},
{
source: 'b',
target: 'c',
value: 1
}
]
};
var testData = {
nodes: [
{
name: 'a'
},
{
name: 'b'
},
{
name: 'a1'
chart.setOption({
color: ['#67001f', '#b2182b', '#d6604d', '#f4a582', '#fddbc7', '#d1e5f0', '#92c5de', '#4393c3', '#2166ac', '#053061'],
tooltip: {
trigger: 'item',
triggerOn: 'mousemove'
},
animation: false,
series: [
{
type: 'sankey',
layout:'none',
bottom: '10%',
// focusNodeAdjacency: 'allEdges',
data: testData.nodes,
links: testData.links,
label: {
position: 'left'
},
{
name: 'b1'
},
{
name: 'c'
},
{
name: 'e'
}
],
links: [
{
source: 'a',
target: 'a1',
value: 5
},
{
source: 'e',
target: 'b',
value: 3
},
{
source: 'a',
target: 'b1',
value: 3
},
{
source: 'b1',
target: 'a1',
value: 1
},
{
source: 'b1',
target: 'c',
value: 2
},
{
source: 'b',
target: 'c',
value: 1
}
]
};
chart.setOption({
color: ['#67001f', '#b2182b', '#d6604d', '#f4a582', '#fddbc7', '#d1e5f0', '#92c5de', '#4393c3', '#2166ac', '#053061'],
tooltip: {
trigger: 'item',
triggerOn: 'mousemove'
},
animation: false,
series: [
{
type: 'sankey',
layout:'none',
bottom: '10%',
data: testData.nodes,
links: testData.links,
// Used to test when the data is null whether it is work well.
// data: [],
// links: [],
lineStyle: {
normal: {
color: 'source',
curveness: 0.5
}
// Used to test when the data is null whether it is work well.
// data: [],
// links: [],
lineStyle: {
normal: {
color: 'source',
curveness: 0.5
}
}
]
});
}
]
});
});
</script>
</body>
</html>
\ No newline at end of file
......@@ -73,6 +73,7 @@ under the License.
{
type: 'sankey',
layout:'none',
focusNodeAdjacency: 'allEdges',
data: data.nodes,
links: data.links,
lineStyle: {
......
<!DOCTYPE html>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="lib/esl.js"></script>
<script src="lib/config.js"></script>
<script src="lib/jquery.min.js"></script>
<script src="lib/facePrint.js"></script>
<script src="lib/testHelper.js"></script>
<link rel="stylesheet" href="lib/reset.css" />
</head>
<body>
<style>
.test-title {
background: #146402;
color: #fff;
}
</style>
<div id="main0"></div>
<script>
require([
'echarts'/*, 'map/js/china' */
], function (echarts) {
var data = [[10, 300], [10, 600]];
var option = {
xAxis: {},
yAxis: {},
tooltip: {
position: function (point, params, dom, rect, size) {
return [rect.x, rect.y];
},
extraCssText: 'width: 60px; height: 60px;'
},
series: [{
id: 'scatter',
type: 'scatter',
symbol: 'rect',
symbolSize: 50,
emphasis: {
itemStyle: {
color: 'green'
}
},
data: data
}]
};
var chart = testHelper.create(echarts, 'main0', {
title: [
'Tooltip should be event disalbed:',
'(1) When hovering on the symbol',
'tooltip is shown on the symbol;',
'the symbol should keep green, but not return to red.',
'(2) Enable moving the symbol without tooltip effection',
'(3) click "set tooltip enterable, button in tooltip should be clickable'
],
option: option,
button: {
text: 'set tooltip enterable',
onclick: function () {
chart.setOption({
tooltip: {
formatter: function () {
return [
'<button onclick="alert(\'click\');">click me</button>'
].join('');
},
enterable: true
}
});
}
}
});
if (chart) {
var zr = chart.getZr();
var pointerOffset;
var draggingDataIndex;
chart.on('mousedown', function (params) {
if (params.seriesIndex === 0) {
var pointerData = chart.convertFromPixel(
'grid', [params.event.offsetX, params.event.offsetY]
);
draggingDataIndex = params.dataIndex;
pointerOffset = [
params.data[0] - pointerData[0],
params.data[1] - pointerData[1]
];
}
});
zr.on('mousemove', function (params) {
if (draggingDataIndex != null) {
var pointerData = chart.convertFromPixel(
'grid', [params.event.offsetX, params.event.offsetY]
);
data[draggingDataIndex] = [
pointerData[0] + pointerOffset[0],
pointerData[1] + pointerOffset[1]
];
chart.setOption({
series: {
id: 'scatter',
data: data
},
animationDurationUpdate: 0
});
}
});
zr.on('mouseup', function (params) {
draggingDataIndex = null;
chart.setOption({
animationDurationUpdate: 700
});
});
}
});
</script>
</body>
</html>
\ No newline at end of file
此差异已折叠。
......@@ -65,7 +65,7 @@ under the License.
type: 'tree',
data: [data],
roam: true,
top: '1%',
left: '7%',
bottom: '1%',
......
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="lib/esl.js"></script>
<script src="lib/config.js"></script>
<script src="lib/jquery.min.js"></script>
</head>
<body>
<style>
html, body, #main {
width: 100%;
padding: 0;
margin: 0;
height: 100%;
}
</style>
<div id="main"></div>
<script>
require([
'echarts'
// 'echarts/chart/tree',
// 'echarts/component/tooltip'
], function (echarts) {
var chart = echarts.init(document.getElementById('main'), null, {
});
window.onresize = function () {
chart.resize();
};
$.getJSON('./data/flare.json')
.done(function (data) {
chart.setOption({
tooltip: {
trigger: 'item',
triggerOn: 'mousemove'
},
series:[
{
type: 'tree',
data: [data],
top: '18%',
bottom: '14%',
roam: true,
layout: 'radial',
symbol: 'emptyCircle',
symbolSize: 7,
initialTreeDepth: 3,
animationDurationUpdate: 750
}
]
});
});
});
</script>
</body>
</html>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册