├── export.js ├── .npmrc ├── .babelrc.json ├── .eslintignore ├── index.d.ts ├── index.js ├── .editorconfig ├── .prettierignore ├── .prettierrc ├── .gitignore ├── lib ├── helper.js ├── AzureMapModel.js ├── index.js ├── AzureMapView.js └── AzureMapCoordSys.js ├── LICENSE ├── .eslintrc.js ├── package.json ├── rollup.config.js ├── examples ├── heatmap.html ├── pie-echart@4.html ├── pie-echart@5.html ├── line.html ├── fly.html └── scatter.html └── README.md /export.js: -------------------------------------------------------------------------------- 1 | export * from './lib/index'; 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org 2 | -------------------------------------------------------------------------------- /.babelrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/env"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | build/ 4 | .eslintrc.js 5 | examples/ 6 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare const name = 'echarts-extension-azure-map'; 2 | declare const version = '1.0.0'; 3 | 4 | export { name, version }; 5 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import * as echarts from 'echarts'; 2 | import { isNewEC } from './lib/helper'; 3 | import { install } from './lib/index'; 4 | 5 | isNewEC ? echarts.use(install) : install(echarts); 6 | 7 | export { name, version } from './lib/index'; 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.svg 2 | /dist 3 | .dockerignore 4 | node_modules 5 | .DS_Store 6 | .eslintignore 7 | *.png 8 | *.toml 9 | docker 10 | .editorconfig 11 | .gitignore 12 | .prettierignore 13 | LICENSE 14 | .eslintcache 15 | *.lock 16 | yarn-error.log 17 | .history 18 | CNAME 19 | package-lock.json 20 | /.vscode 21 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "proseWrap": "never", 6 | "endOfLine": "lf", 7 | "overrides": [{ "files": ".prettierrc", "options": { "parser": "json" } }], 8 | "plugins": ["prettier-plugin-organize-imports", "prettier-plugin-packagejson"] 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist-ssr 12 | *.local 13 | 14 | # Editor directories and files 15 | .vscode/* 16 | !.vscode/extensions.json 17 | .idea 18 | .DS_Store 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /lib/helper.js: -------------------------------------------------------------------------------- 1 | import { version } from 'echarts'; 2 | 3 | export const ecVer = version.split('.'); 4 | 5 | export const isNewEC = ecVer[0] > 4; 6 | 7 | export const COMPONENT_TYPE = 'azuremap'; 8 | 9 | export const ACTION_TYPE = `${COMPONENT_TYPE}Roam`; 10 | 11 | export function v2Equal(a, b) { 12 | return a && b && a[0] === b[0] && a[1] === b[1]; 13 | } 14 | -------------------------------------------------------------------------------- /lib/AzureMapModel.js: -------------------------------------------------------------------------------- 1 | import { ComponentModel } from 'echarts'; 2 | import { COMPONENT_TYPE, isNewEC, v2Equal } from './helper'; 3 | 4 | const AzureMapModel = { 5 | type: COMPONENT_TYPE, 6 | setAzureMap: function (azuremap) { 7 | this.__azuremap = azuremap; 8 | }, 9 | getAzureMap: function () { 10 | // __azuremap is injected when creating AzureMapCoordSys 11 | return this.__azuremap; 12 | }, 13 | setEChartsLayer: function (layer) { 14 | this.__echartsLayer = layer; 15 | }, 16 | getEChartsLayer: function () { 17 | return this.__echartsLayer; 18 | }, 19 | setCenterAndZoom: function (center, zoom) { 20 | this.option.center = center; 21 | this.option.zoom = zoom; 22 | }, 23 | centerOrZoomChanged: function (center, zoom) { 24 | const option = this.option; 25 | return !(v2Equal(center, option.center) && zoom === option.zoom); 26 | }, 27 | defaultOption: { 28 | view: 'Auto', 29 | center: [104.1064453125, 37.54457732085582], 30 | zoom: 5, 31 | }, 32 | }; 33 | 34 | export default isNewEC ? ComponentModel.extend(AzureMapModel) : AzureMapModel; 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 andybuibui 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { browser: true, es6: true, node: true, es2020: true }, 3 | extends: 'eslint:recommended', 4 | parserOptions: { 5 | ecmaVersion: 2020, 6 | sourceType: 'module', 7 | }, 8 | rules: { 9 | strict: 0, 10 | 'no-param-reassign': 2, 11 | 'no-undef': 2, 12 | 'accessor-pairs': 2, 13 | 'comma-dangle': [2, 'always-multiline'], 14 | 'consistent-return': 2, 15 | 'dot-notation': 2, 16 | eqeqeq: 2, 17 | indent: [2, 2, { SwitchCase: 1 }], 18 | 'no-cond-assign': 2, 19 | 'no-constant-condition': 2, 20 | 'no-eval': 2, 21 | 'no-inner-declarations': [0], 22 | 'no-unneeded-ternary': 2, 23 | radix: 2, 24 | semi: [2, 'always'], 25 | 'comma-spacing': 2, 26 | 'comma-style': 2, 27 | 'eol-last': 2, 28 | 'linebreak-style': [2, 'unix'], 29 | 'new-parens': 2, 30 | 'no-lonely-if': 2, 31 | 'no-multiple-empty-lines': 2, 32 | 'no-nested-ternary': 2, 33 | 'no-spaced-func': 2, 34 | 'no-trailing-spaces': 2, 35 | 'operator-linebreak': 2, 36 | quotes: [2, 'single'], 37 | 'semi-spacing': 2, 38 | 'space-unary-ops': 2, 39 | 'arrow-parens': 2, 40 | 'arrow-spacing': 2, 41 | 'no-class-assign': 2, 42 | 'no-dupe-class-members': 2, 43 | 'no-var': 2, 44 | 'object-shorthand': 0, 45 | 'prefer-const': 2, 46 | 'prefer-spread': 2, 47 | 'prefer-template': 2, 48 | }, 49 | }; 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "echarts-extension-azure-map", 3 | "version": "2.0.0", 4 | "private": false, 5 | "description": "Azure Map for Apache ECharts (https://github.com/apache/echarts)", 6 | "keywords": [ 7 | "echarts", 8 | "azure-map", 9 | "echarts-extension", 10 | "map", 11 | "azuremap", 12 | "echarts-azure-map", 13 | "echarts4", 14 | "echarts5", 15 | "azure", 16 | "echarts-extension-azure-map" 17 | ], 18 | "homepage": "https://github.com/andybuibui/echarts-extension-azure-map#readme", 19 | "bugs": { 20 | "url": "https://github.com/andybuibui/echarts-extension-azure-map/issues" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/andybuibui/echarts-extension-azure-map" 25 | }, 26 | "license": "MIT", 27 | "author": "andybuibui", 28 | "main": "dist/echarts-extension-azure-map.min.js", 29 | "module": "dist/echarts-extension-azure-map.esm.js", 30 | "types": "index.d.ts", 31 | "files": [ 32 | "dist", 33 | "index.d.ts" 34 | ], 35 | "scripts": { 36 | "build": "rollup -c --bundleConfigAsCjs", 37 | "format": "prettier --cache --write ." 38 | }, 39 | "devDependencies": { 40 | "@babel/core": "^7.24.5", 41 | "@babel/preset-env": "^7.24.5", 42 | "@rollup/plugin-babel": "^6.0.4", 43 | "@rollup/plugin-commonjs": "^25.0.7", 44 | "@rollup/plugin-json": "^6.1.0", 45 | "@rollup/plugin-node-resolve": "^15.2.3", 46 | "@rollup/plugin-terser": "^0.4.4", 47 | "@types/echarts": "^4.9.22", 48 | "azure-maps-control": "^3.1.2", 49 | "chalk": "^4.1.2", 50 | "echarts": "^5.5.0", 51 | "eslint": "^8.57.0", 52 | "globals": "^15.8.0", 53 | "prettier": "^3.2.5", 54 | "prettier-plugin-organize-imports": "^3.2.2", 55 | "prettier-plugin-packagejson": "^2.4.3", 56 | "rollup": "^4.17.2" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | import AzureMapCoordSys from './AzureMapCoordSys'; 2 | import AzureMapModel from './AzureMapModel'; 3 | import AzureMapView from './AzureMapView'; 4 | import { ACTION_TYPE, COMPONENT_TYPE, ecVer, isNewEC } from './helper'; 5 | 6 | export { name, version } from '../package.json'; 7 | 8 | export function install(registers) { 9 | // add coordinate system support for pie series for ECharts < 5.4.0 10 | if (!isNewEC || (ecVer[0] === 5 && ecVer[1] < 4)) { 11 | registers.registerLayout(function (ecModel) { 12 | ecModel.eachSeriesByType('pie', function (seriesModel) { 13 | const coordSys = seriesModel.coordinateSystem; 14 | const data = seriesModel.getData(); 15 | const valueDim = data.mapDimension('value'); 16 | if (coordSys && coordSys.type === COMPONENT_TYPE) { 17 | const center = seriesModel.get('center'); 18 | const point = coordSys.dataToPoint(center); 19 | const cx = point[0]; 20 | const cy = point[1]; 21 | data.each(valueDim, function (_value, idx) { 22 | const layout = data.getItemLayout(idx); 23 | layout.cx = cx; 24 | layout.cy = cy; 25 | }); 26 | } 27 | }); 28 | }); 29 | } 30 | // Model 31 | isNewEC 32 | ? registers.registerComponentModel(AzureMapModel) 33 | : registers.extendComponentModel(AzureMapModel); 34 | // View 35 | isNewEC 36 | ? registers.registerComponentView(AzureMapView) 37 | : registers.extendComponentView(AzureMapView); 38 | // Coordinate System 39 | registers.registerCoordinateSystem(COMPONENT_TYPE, AzureMapCoordSys); 40 | // Action 41 | registers.registerAction( 42 | { 43 | type: ACTION_TYPE, 44 | event: ACTION_TYPE, 45 | update: 'updateLayout', 46 | }, 47 | function (_payload, ecModel) { 48 | ecModel.eachComponent(COMPONENT_TYPE, function (azMapModel) { 49 | const azmap = azMapModel.getAzureMap(); 50 | const center = azmap.getCamera().center; 51 | const zoom = azmap.getCamera().zoom; 52 | azMapModel.setCenterAndZoom(center, zoom); 53 | }); 54 | }, 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import { babel } from '@rollup/plugin-babel'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import json from '@rollup/plugin-json'; 4 | import { nodeResolve } from '@rollup/plugin-node-resolve'; 5 | import terser from '@rollup/plugin-terser'; 6 | import chalk from 'chalk'; 7 | import fs from 'node:fs'; 8 | import path from 'node:path'; 9 | import { author, name, version } from './package.json'; 10 | 11 | const resolve = (p) => path.resolve(__dirname, p); 12 | function getLicense() { 13 | const license = fs.readFileSync(path.resolve(__dirname, './LICENSE'), { encoding: 'utf-8' }); 14 | const content = `${name} \n@version ${version}\n@author ${author}\n\n${license}`; 15 | return `/*!\n * ${content.replace(/\*\//g, '* /').split('\n').join('\n * ')}\n */`; 16 | } 17 | 18 | const outputConfigs = { 19 | esm: { 20 | file: resolve(`dist/${name}.esm.js`), 21 | format: 'es', 22 | }, 23 | cjs: { 24 | file: resolve(`dist/${name}.cjs.js`), 25 | format: 'cjs', 26 | }, 27 | umd: { 28 | file: resolve(`dist/${name}.js`), 29 | format: 'umd', 30 | }, 31 | mumd: { 32 | file: resolve(`dist/${name}.min.js`), 33 | format: 'umd', 34 | }, 35 | }; 36 | 37 | console.log(chalk.bgCyan(`🚩 Building ${version} ... `)); 38 | 39 | const packageConfigs = Object.keys(outputConfigs).reduce((prev, format) => { 40 | const output = { 41 | ...outputConfigs[format], 42 | externalLiveBindings: false, 43 | interop: 'esModule', 44 | globals: { 45 | echarts: 'echarts', 46 | atlas: 'atlas', 47 | }, 48 | validate: true, 49 | banner: getLicense(), 50 | }; 51 | const plugins = [ 52 | json(), 53 | nodeResolve(), 54 | commonjs(), 55 | babel({ 56 | babelHelpers: 'bundled', 57 | compact: false, 58 | }), 59 | ]; 60 | if (output.format === 'umd') { 61 | output.name = 'echarts.azuremap'; 62 | output.sourcemap = true; 63 | } 64 | if (format === 'mumd') { 65 | plugins.push( 66 | terser({ 67 | module: /^esm/.test(format), 68 | compress: { 69 | ecma: 2015, 70 | pure_getters: true, 71 | pure_funcs: ['console.log'], 72 | }, 73 | safari10: true, 74 | ie8: false, 75 | }), 76 | ); 77 | } 78 | 79 | return prev.concat({ 80 | input: resolve('index.js'), 81 | external: ['echarts', 'atlas'], 82 | plugins: [...plugins], 83 | output, 84 | treeshake: { 85 | moduleSideEffects: false, 86 | }, 87 | }); 88 | }, []); 89 | export default packageConfigs; 90 | -------------------------------------------------------------------------------- /lib/AzureMapView.js: -------------------------------------------------------------------------------- 1 | import { ComponentView } from 'echarts'; 2 | import { ACTION_TYPE, COMPONENT_TYPE, isNewEC } from './helper'; 3 | 4 | const AzureMapView = { 5 | type: COMPONENT_TYPE, 6 | render: function (azMapModel, ecModel, api) { 7 | let rendering = true; 8 | 9 | const azmap = azMapModel.getAzureMap(); 10 | const viewportRoot = api.getZr().painter.getViewportRoot(); 11 | const coordSys = azMapModel.coordinateSystem; 12 | const moveHandler = function () { 13 | if (rendering) { 14 | return; 15 | } 16 | const offsetEl = azmap.getMapContainer(); 17 | const mapOffset = [ 18 | -parseInt(offsetEl.style.left, 10) || 0, 19 | -parseInt(offsetEl.style.top, 10) || 0, 20 | ]; 21 | const viewportRootStyle = viewportRoot.style; 22 | const offsetLeft = `${mapOffset[0]}px`; 23 | const offsetTop = `${mapOffset[1]}px`; 24 | if (viewportRootStyle.left !== offsetLeft) { 25 | viewportRootStyle.left = offsetLeft; 26 | } 27 | if (viewportRootStyle.top !== offsetTop) { 28 | viewportRootStyle.top = offsetTop; 29 | } 30 | coordSys.setMapOffset(mapOffset); 31 | azMapModel.__mapOffset = mapOffset; 32 | api.dispatchAction({ 33 | type: ACTION_TYPE, 34 | animation: { 35 | duration: 0, 36 | }, 37 | }); 38 | }; 39 | 40 | function zoomEndHandler() { 41 | if (rendering) { 42 | return; 43 | } 44 | api.dispatchAction({ 45 | type: ACTION_TYPE, 46 | animation: { 47 | duration: 0, 48 | }, 49 | }); 50 | } 51 | 52 | azmap.events.remove('move', this._oldMoveHandler); 53 | azmap.events.remove('moveend', this._oldMoveHandler); 54 | azmap.events.remove('zoomend', this._oldZoomEndHandler); 55 | 56 | azmap.events.add('move', moveHandler); 57 | azmap.events.add('moveend', moveHandler); 58 | azmap.events.add('zoomend', zoomEndHandler); 59 | 60 | this._oldMoveHandler = moveHandler; 61 | this._oldZoomEndHandler = zoomEndHandler; 62 | rendering = false; 63 | }, 64 | dispose: function () { 65 | const component = this.__model; 66 | delete this._oldMoveHandler; 67 | delete this._oldZoomEndHandler; 68 | if (component) { 69 | component.getAzureMap().dispose(); 70 | component.setAzureMap(null); 71 | component.getEChartsLayer(null); 72 | if (component.coordinateSystem) { 73 | component.coordinateSystem.setAzureMap(null); 74 | component.coordinateSystem = null; 75 | } 76 | } 77 | }, 78 | }; 79 | 80 | export default isNewEC ? ComponentView.extend(AzureMapView) : AzureMapView; 81 | -------------------------------------------------------------------------------- /examples/heatmap.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | echarts extension azuremaps 8 | 12 | 13 | 14 | 18 | 19 | 33 | 34 | 35 |
36 |
37 |
38 | 39 | 96 | 97 | -------------------------------------------------------------------------------- /examples/pie-echart@4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | echarts extension azuremaps 8 | 12 | 13 | 14 | 18 | 19 | 33 | 34 | 35 |
36 |
37 |
38 | 39 | 109 | 110 | -------------------------------------------------------------------------------- /examples/pie-echart@5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | echarts extension azuremaps 8 | 12 | 13 | 14 | 18 | 19 | 33 | 34 | 35 |
36 |
37 |
38 | 39 | 109 | 110 | -------------------------------------------------------------------------------- /examples/line.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | echarts extension azuremaps 8 | 12 | 13 | 14 | 18 | 19 | 33 | 34 | 35 |
36 |
37 |
38 | 39 | 123 | 124 | -------------------------------------------------------------------------------- /lib/AzureMapCoordSys.js: -------------------------------------------------------------------------------- 1 | import * as atlas from 'azure-maps-control'; 2 | import { graphic, matrix, util as zrUtil } from 'echarts'; 3 | import { COMPONENT_TYPE } from './helper'; 4 | function dataToCoordSize(dataSize, dataItem) { 5 | const finalDataItem = dataItem || [0, 0]; 6 | return zrUtil.map( 7 | [0, 1], 8 | function (dimIdx) { 9 | const val = finalDataItem[dimIdx]; 10 | const halfSize = dataSize[dimIdx] / 2; 11 | const p1 = []; 12 | const p2 = []; 13 | p1[dimIdx] = val - halfSize; 14 | p2[dimIdx] = val + halfSize; 15 | p1[1 - dimIdx] = p2[1 - dimIdx] = finalDataItem[1 - dimIdx]; 16 | return Math.abs(this.dataToPoint(p1)[dimIdx] - this.dataToPoint(p2)[dimIdx]); 17 | }, 18 | this, 19 | ); 20 | } 21 | 22 | function AzureMapCoordSys(azuremap, api) { 23 | this._azuremap = azuremap; 24 | this.dimensions = ['lng', 'lat']; 25 | this._mapOffset = [0, 0]; 26 | this._api = api; 27 | } 28 | 29 | const AzureMapCoordSysProto = AzureMapCoordSys.prototype; 30 | 31 | AzureMapCoordSysProto.type = COMPONENT_TYPE; 32 | 33 | AzureMapCoordSysProto.dimensions = ['lng', 'lat']; 34 | 35 | AzureMapCoordSysProto.setZoom = function (zoom) { 36 | this._zoom = zoom; 37 | }; 38 | 39 | AzureMapCoordSysProto.setCenter = function (center) { 40 | //Format coordinates as longitude, latitude. 41 | const latlng = atlas.data.Position.fromLatLng(center[0], center[1]); 42 | // Represent a pixel coordinate or offset. Extends an array of [x, y]. 43 | const [x, y] = this._azuremap.positionsToPixels([latlng])[0]; 44 | this._center = { x, y }; 45 | }; 46 | 47 | AzureMapCoordSysProto.setMapOffset = function (mapOffset) { 48 | this._mapOffset = mapOffset; 49 | }; 50 | 51 | AzureMapCoordSysProto.setAzureMap = function (azmap) { 52 | this._azuremap = azmap; 53 | }; 54 | 55 | AzureMapCoordSysProto.getAzureMap = function () { 56 | return this._azuremap; 57 | }; 58 | 59 | AzureMapCoordSysProto.dataToPoint = function (data) { 60 | const latlng = atlas.data.Position.fromLatLng(data); 61 | const [x, y] = this._azuremap.positionsToPixels([latlng])[0]; 62 | const mapOffset = this._mapOffset; 63 | return [x - mapOffset[0], y - mapOffset[1]]; 64 | }; 65 | 66 | AzureMapCoordSysProto.pointToData = function (pt) { 67 | const mapOffset = this._mapOffset; 68 | // https://learn.microsoft.com/zh-cn/javascript/api/azure-maps-control/atlas.map?view=azure-maps-typescript-latest#azure-maps-control-atlas-map-pixelstopositions 69 | const [lat, lng] = this._azuremap.pixelsToPositions([ 70 | new atlas.Pixel(pt[0] + mapOffset[0], pt[1] + mapOffset[1]), 71 | ])[0]; 72 | return [lng, lat]; 73 | }; 74 | 75 | AzureMapCoordSysProto.getViewRect = function () { 76 | const api = this._api; 77 | return new graphic.BoundingRect(0, 0, api.getWidth(), api.getHeight()); 78 | }; 79 | 80 | AzureMapCoordSysProto.getRoamTransform = function () { 81 | return matrix.create(); 82 | }; 83 | 84 | AzureMapCoordSysProto.prepareCustoms = function () { 85 | const rect = this.getViewRect(); 86 | return { 87 | coordSys: { 88 | // The name exposed to user is always 'cartesian2d' but not 'grid'. 89 | type: COMPONENT_TYPE, 90 | x: rect.x, 91 | y: rect.y, 92 | width: rect.width, 93 | height: rect.height, 94 | }, 95 | api: { 96 | coord: zrUtil.bind(this.dataToPoint, this), 97 | size: zrUtil.bind(dataToCoordSize, this), 98 | }, 99 | }; 100 | }; 101 | 102 | AzureMapCoordSysProto.convertToPixel = function (_ecModel, _finder, value) { 103 | // here we ignore finder as only one azuremap component is allowed 104 | return this.dataToPoint(value); 105 | }; 106 | 107 | AzureMapCoordSysProto.convertFromPixel = function (_ecModel, _finder, value) { 108 | return this.pointToData(value); 109 | }; 110 | 111 | // For deciding which dimensions to use when creating list data 112 | AzureMapCoordSys.dimensions = AzureMapCoordSysProto.dimensions; 113 | 114 | // let Overlay; 115 | AzureMapCoordSys.create = function (ecModel, api) { 116 | let azureMapCoordSys; 117 | const root = api.getDom(); 118 | 119 | ecModel.eachComponent(COMPONENT_TYPE, function (azuremapModel) { 120 | const painter = api.getZr().painter; 121 | const viewportRoot = painter.getViewportRoot(); 122 | // Overlay = 123 | // Overlay || 124 | // new atlas.layer.WebGLLayer('ec-map-layer', { 125 | // renderer: { 126 | // onAdd: function (mapInstance) { 127 | // mapInstance.getCanvasContainer().appendChild(viewportRoot); 128 | // }, 129 | // render: function () {}, 130 | // }, 131 | // }); 132 | if (azureMapCoordSys) { 133 | throw new Error('Only one azuremap component can exist'); 134 | } 135 | let azuremap = azuremapModel.getAzureMap(); 136 | if (!azuremap) { 137 | let azuremapRoot = root.querySelector('.ec-extension-azure-map'); 138 | viewportRoot.className = 'azure-ec-layer'; 139 | viewportRoot.style.pointerEvents = 'auto'; 140 | viewportRoot.style.position = 'absolute'; 141 | viewportRoot.style.display = 'none'; 142 | viewportRoot.style.left = '0px'; 143 | viewportRoot.style.top = '0px'; 144 | if (azuremapRoot) { 145 | viewportRoot.style.left = '0px'; 146 | viewportRoot.style.top = '0px'; 147 | root.removeChild(azuremapRoot); 148 | } 149 | azuremapRoot = document.createElement('div'); 150 | azuremapRoot.style.cssText = 'position:absolute;top:0;left:0;right:0;bottom:0;'; 151 | azuremapRoot.className = 'ec-extension-azure-map'; 152 | 153 | root.appendChild(azuremapRoot); 154 | 155 | let mapOptions = azuremapModel.get(); 156 | if (mapOptions) { 157 | mapOptions = zrUtil.clone(mapOptions); 158 | } 159 | azuremap = new atlas.Map(azuremapRoot, mapOptions); 160 | azuremapModel.setAzureMap(azuremap); 161 | azuremap.events.add('ready', function () { 162 | azuremap.getCanvasContainer().appendChild(viewportRoot); 163 | viewportRoot.style.display = ''; 164 | // azuremap.layers.add(Overlay); 165 | }); 166 | 167 | // Override 168 | painter.getViewportRootOffset = function () { 169 | return { offsetLeft: 0, offsetTop: 0 }; 170 | }; 171 | } 172 | // Set azuremap options 173 | // centerAndZoom before layout and render 174 | const center = azuremapModel.get('center'); 175 | const zoom = azuremapModel.get('zoom'); 176 | if (center && zoom) { 177 | const azuremapCenter = azuremap.getCamera().center; 178 | const azuremapZoom = azuremap.getCamera().zoom; 179 | const centerOrZoomChanged = azuremapModel.centerOrZoomChanged(azuremapCenter, azuremapZoom); 180 | if (centerOrZoomChanged) { 181 | azuremap.setCamera({ center, zoom }); 182 | } 183 | } 184 | 185 | azureMapCoordSys = new AzureMapCoordSys(azuremap, api); 186 | azureMapCoordSys.setMapOffset(azuremapModel.__mapOffset || [0, 0]); 187 | azureMapCoordSys.setZoom(zoom); 188 | azureMapCoordSys.setCenter(center); 189 | azuremapModel.coordinateSystem = azureMapCoordSys; 190 | }); 191 | 192 | ecModel.eachSeries(function (seriesModel) { 193 | if (seriesModel.get('coordinateSystem') === COMPONENT_TYPE) { 194 | seriesModel.coordinateSystem = azureMapCoordSys; 195 | } 196 | }); 197 | 198 | return azureMapCoordSys && [azureMapCoordSys]; 199 | }; 200 | 201 | export default AzureMapCoordSys; 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Echarts-Extension-Azure-Map 2 | [![NPM version](https://img.shields.io/npm/v/echarts-extension-azure-map.svg?style=flat)](https://www.npmjs.org/package/echarts-extension-azure-map) [![NPM Downloads](https://img.shields.io/npm/dm/echarts-extension-azure-map.svg)](https://npmcharts.com/compare/echarts-extension-azure-map?minimal=true) [![jsDelivr Downloads](https://data.jsdelivr.com/v1/package/npm/echarts-extension-azure-map/badge?style=rounded)](https://www.jsdelivr.com/package/npm/echarts-extension-azure-map) 3 | 4 | ## Azure Map Extension for Apache ECharts 5 | 6 | `Echarts Extension Azure Maps` is an Extension for [Apache ECharts](https://echarts.apache.org/en/index.html). 7 | 8 | ## Installation 9 | Use the package manager `npm` or `yarn` 10 | 11 | ```bash 12 | npm install echarts-extension-azure-map 13 | ``` 14 | 15 | or 16 | 17 | ```bash 18 | yarn add echarts-extension-azure-map 19 | ``` 20 | 21 | ## Styling 22 | Embed the following css to your application. The stylesheet is required for the marker, popup and control components in `react-azure-maps` to work properly. 23 | ```javascript 24 | import 'azure-maps-control/dist/atlas.min.css' 25 | ``` 26 | 27 | ## Authentication 28 | 29 | The subscription key is intended for development environments only and must not be utilized in a production application. Azure Maps provides various authentication options for applications to use. See [here](https://learn.microsoft.com/en-us/azure/azure-maps/how-to-manage-authentication) for more details. 30 | 31 | ```javascript 32 | // AAD 33 | authOptions: { 34 | authType: AuthenticationType.aad, 35 | clientId: '...', 36 | aadAppId: '...', 37 | aadTenant: '...' 38 | } 39 | ``` 40 | 41 | ```javascript 42 | // Anonymous 43 | authOptions: { 44 | authType: AuthenticationType.anonymous, 45 | clientId: '...', 46 | getToken: (resolve, reject) => { 47 | // URL to your authentication service that retrieves an Azure Active Directory Token. 48 | var tokenServiceUrl = "https://example.com/api/GetAzureMapsToken"; 49 | fetch(tokenServiceUrl).then(r => r.text()).then(token => resolve(token)); 50 | } 51 | } 52 | ``` 53 | 54 | ```javascript 55 | // SAS Token 56 | authOptions: { 57 | authType: AuthenticationType.sas, 58 | getToken: (resolve, reject) => { 59 | // URL to your authentication service that retrieves a SAS Token. 60 | var tokenServiceUrl = "https://example.com/api/GetSASToken"; 61 | fetch(tokenServiceUrl).then(r => r.text()).then(token => resolve(token)); 62 | } 63 | } 64 | ``` 65 | 66 | ## Import 67 | 68 | Import packaged distribution file `echarts-extension-azure-map` and add Azure Map API script and ECharts script. 69 | 70 | ```html 71 | 75 | 76 | 77 | 78 | 79 | 80 | ``` 81 | 82 | You can also import this extension by `require` or `import` if you are using `webpack` or any other bundler. 83 | 84 | ```javascript 85 | // use require 86 | require('echarts'); 87 | require('echarts-extension-azure-map'); 88 | require('azure-maps-control/dist/atlas.min.css'); 89 | require('azure-maps-control'); 90 | 91 | // use import 92 | import * as echarts from 'echarts'; 93 | import 'echarts-extension-azure-map'; 94 | import "azure-maps-control/dist/atlas.min.css"; 95 | import * as atlas from 'azure-maps-control'; 96 | ``` 97 | 98 | ## eg: use subscriptionKey 99 | 100 | ```javascript 101 | // app.ts or index.js 102 | import "azure-maps-control/dist/atlas.min.css"; 103 | 104 | // page.tsx 105 | import * as echarts from 'echarts'; 106 | import { useEffect, useRef } from 'react'; 107 | import { AuthenticationType } from 'azure-maps-control'; 108 | 109 | export default function App() { 110 | const ref = useRef(null); 111 | const loadMap = () => { 112 | const option = { 113 | azuremap: { 114 | center: [104.1064453125, 37.54457732085582], 115 | zoom: 5, 116 | view: 'Auto', 117 | language: 'en-US', 118 | authOptions: { 119 | authType: AuthenticationType.subscriptionKey, 120 | subscriptionKey: 'your subscriptionKey', 121 | }, 122 | }, 123 | series: [], 124 | } 125 | const chart = echarts.init(ref.current); 126 | chart.setOption(option); 127 | } 128 | useEffect(() => { 129 | loadMap(); 130 | }, []); 131 | return
; 132 | } 133 | ``` 134 | 135 | ## eg: use anonymous and clientId 136 | 137 | ```javascript 138 | // app.ts or index.js 139 | import "azure-maps-control/dist/atlas.min.css"; 140 | 141 | // page.tsx 142 | import * as echarts from 'echarts'; 143 | import { useEffect, useRef } from 'react'; 144 | import { AuthenticationType } from 'azure-maps-control'; 145 | 146 | export default function App() { 147 | const ref = useRef(null); 148 | const loadMap = () => { 149 | const option = { 150 | azuremap: { 151 | center: [104.1064453125, 37.54457732085582], 152 | zoom: 5, 153 | view: 'Auto', 154 | language: 'en-US', 155 | authOptions: { 156 | authType: AuthenticationType.anonymous, 157 | clientId: 'your client id', 158 | getToken: function (resolve, reject, map) { 159 | //URL to your authentication service that retrieves an Microsoft Entra ID Token. 160 | const tokenServiceUrl = 'https://your-backend-server/api/GetAzureMapsToken'; 161 | fetch(tokenServiceUrl).then(r => r.text()).then(token => resolve(token)); 162 | }, 163 | }, 164 | }, 165 | series: [], 166 | } 167 | const chart = echarts.init(ref.current); 168 | chart.setOption(option); 169 | } 170 | useEffect(() => { 171 | loadMap(); 172 | }, []); 173 | return
; 174 | } 175 | ``` 176 | 177 | ## Examples 178 | 179 | #### FlyChart [examples/fly.html](https://github.com/andybuibui/echarts-extension-azure-map/blob/main/examples/fly.html) 180 | ![image](https://github.com/user-attachments/assets/3af869ef-60a2-4c66-86f7-a0576bcfaa71) 181 | 182 | ### Heart Chart [examples/heatmap.html](https://github.com/andybuibui/echarts-extension-azure-map/blob/main/examples/heatmap.html) 183 | ![image](https://github.com/user-attachments/assets/2013714a-b5da-44f8-9f2a-146c9983a771) 184 | 185 | #### Line Chart [examples/line.html](https://github.com/andybuibui/echarts-extension-azure-map/blob/main/examples/line.html) 186 | ![image](https://github.com/user-attachments/assets/402e1268-2ea6-409c-80f6-7e928f385dc5) 187 | 188 | #### Pie Chart for echarts < 5.4.0 [examples/pie-echart@4.html](https://github.com/andybuibui/echarts-extension-azure-map/blob/main/examples/pie-echart%404.html) 189 | ![image](https://github.com/user-attachments/assets/e9680876-4bb0-4d6f-b133-74524919b79a) 190 | 191 | #### Pie Chart for echarts >= 5.4.0 [examples/pie-echart@5.html](https://github.com/andybuibui/echarts-extension-azure-map/blob/main/examples/pie-echart%405.html) 192 | ![image](https://github.com/user-attachments/assets/72117a8d-e282-4568-ac17-6d2408285a33) 193 | 194 | #### Scatter Chart [examples/scatter.html](https://github.com/andybuibui/echarts-extension-azure-map/blob/main/examples/scatter.html) 195 | ![image](https://github.com/user-attachments/assets/9a6861ad-1927-4618-bf56-1b7066a646ff) 196 | 197 | ## More Links 198 | - [echarts-extension-bingmaps](https://github.com/andybuibui/echarts-extension-bingmaps) 199 | - [echarts-extension-amap](https://github.com/plainheart/echarts-extension-amap) 200 | - [echarts-extension-gmap](https://github.com/plainheart/echarts-extension-gmap) 201 | - [echarts-extension-bmap](https://github.com/apache/echarts/blob/master/extension-src/bmap/bmap.ts) 202 | 203 | ## Contributing 204 | 205 | Pull requests are welcomed. For major changes, please open an issue first to discuss what you would like to change. 206 | 207 | ## Creators ✨ 208 | 209 | 210 | 211 | 212 | 213 | 214 | 223 | 224 |
215 |
andybuibui
222 |
225 | 226 | 227 | 228 | 229 | 230 | 231 | ## License 232 | 233 | [MIT](https://choosealicense.com/licenses/mit/) 234 | -------------------------------------------------------------------------------- /examples/fly.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | echarts extension azuremaps 8 | 12 | 13 | 14 | 18 | 19 | 36 | 37 | 38 |
39 |
40 |
41 | 42 | 614 | 615 | -------------------------------------------------------------------------------- /examples/scatter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | echarts extension azuremaps 8 | 12 | 13 | 14 | 18 | 19 | 33 | 34 | 35 |
36 |
37 |
38 | 39 | 526 | 527 | --------------------------------------------------------------------------------