├── .editorconfig ├── .ember-cli ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .prettierignore ├── .prettierrc.js ├── .template-lintrc.js ├── .travis.yml ├── .watchmanconfig ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── addon ├── .gitkeep ├── components │ ├── gmm-result-item.hbs │ ├── gmm-result-item.js │ ├── gmm-sortable-results.hbs │ ├── gmm-sortable-results.js │ ├── gmm-tool-options.hbs │ ├── gmm-tool-options.js │ ├── google-maps-markup.hbs │ └── google-maps-markup.js ├── helpers │ ├── is-equal.js │ └── tool-id.js ├── services │ └── markup-data.js └── utils │ ├── area.js │ ├── create-circle.js │ ├── create-feature.js │ ├── distance.js │ ├── dynamic-label.js │ ├── feature-center.js │ ├── format-number.js │ ├── get-measurement.js │ ├── guid.js │ ├── init-measure-label.js │ ├── init-text-label.js │ ├── label-plotter.js │ ├── layer.js │ ├── map-label.js │ ├── measure-type-result.js │ ├── modes.js │ ├── number-commas.js │ ├── options-data.js │ ├── overlay-to-feature.js │ ├── path-distance.js │ ├── paths-to-bounds.js │ ├── shape-area.js │ └── tools.js ├── app ├── .gitkeep ├── components │ ├── gmm-result-item.js │ ├── gmm-sortable-results.js │ ├── gmm-tool-options.js │ └── google-maps-markup.js ├── helpers │ ├── is-equal.js │ ├── present.js │ └── tool-id.js ├── services │ └── markup-data.js ├── styles │ └── app.less └── utils │ ├── create-circle.js │ ├── create-feature.js │ ├── distance.js │ ├── feature-center.js │ ├── format-number.js │ ├── get-measurement.js │ ├── guid.js │ ├── init-measure-label.js │ ├── label-plotter.js │ ├── map-label.js │ ├── measure-type-result.js │ ├── modes.js │ ├── number-commas.js │ ├── options-data.js │ ├── overlay-to-feature.js │ ├── path-distance.js │ ├── paths-to-bounds.js │ ├── shape-area.js │ └── square-miles.js ├── codemods.log ├── config ├── ember-try.js └── environment.js ├── ember-cli-build.js ├── index.js ├── jsconfig.json ├── package.json ├── public ├── images │ └── spotlight-poi-highlighted_hdpi.png └── netlify.toml ├── testem.js ├── tests ├── dummy │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ ├── .gitkeep │ │ │ └── google-map │ │ │ │ ├── component.js │ │ │ │ └── template.hbs │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── application.js │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── router.js │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── app.less │ │ └── templates │ │ │ └── application.hbs │ ├── config │ │ ├── ember-cli-update.json │ │ ├── environment.js │ │ ├── optional-features.json │ │ └── targets.js │ └── public │ │ └── robots.txt ├── helpers │ └── .gitkeep ├── index.html ├── integration │ └── components │ │ ├── gmm-result-item │ │ └── component-test.js │ │ ├── gmm-sortable-results │ │ └── component-test.js │ │ ├── gmm-tool-options │ │ └── component-test.js │ │ └── google-maps-markup │ │ └── component-test.js ├── test-helper.js └── unit │ ├── .gitkeep │ ├── helpers │ ├── is-equal-test.js │ └── tool-id-test.js │ ├── services │ └── markup-data-test.js │ └── utils │ ├── area-test.js │ ├── create-circle-test.js │ ├── create-feature-test.js │ ├── distance-test.js │ ├── feature-center-test.js │ ├── format-number-test.js │ ├── get-measurement-test.js │ ├── guid-test.js │ ├── init-measure-label-test.js │ ├── label-plotter-test.js │ ├── map-label-test.js │ ├── measure-type-result-test.js │ ├── modes-test.js │ ├── number-commas-test.js │ ├── options-data-test.js │ ├── overlay-to-feature-test.js │ ├── path-distance-test.js │ ├── paths-to-bounds-test.js │ └── shape-area-test.js ├── vendor ├── .gitkeep └── google-maps-markup │ └── styles.css └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.hbs] 16 | insert_final_newline = false 17 | 18 | [*.{diff,md}] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | !.* 16 | .eslintcache 17 | 18 | # ember-try 19 | /.node_modules.ember-try/ 20 | /bower.json.ember-try 21 | /package.json.ember-try 22 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | ecmaVersion: 2018, 8 | sourceType: 'module', 9 | ecmaFeatures: { 10 | legacyDecorators: true, 11 | }, 12 | }, 13 | plugins: ['ember'], 14 | extends: [ 15 | 'eslint:recommended', 16 | 'plugin:ember/recommended', 17 | 'plugin:prettier/recommended', 18 | ], 19 | env: { 20 | browser: true, 21 | }, 22 | globals: { 23 | google: true, 24 | }, 25 | rules: { 26 | 'ember/no-jquery': 0, 27 | }, 28 | overrides: [ 29 | // node files 30 | { 31 | files: [ 32 | '.eslintrc.js', 33 | '.prettierrc.js', 34 | '.template-lintrc.js', 35 | '.prettierrc.js', 36 | 'ember-cli-build.js', 37 | 'index.js', 38 | 'testem.js', 39 | 'blueprints/*/index.js', 40 | 'config/**/*.js', 41 | 'tests/dummy/config/**/*.js', 42 | ], 43 | excludedFiles: [ 44 | 'addon/**', 45 | 'addon-test-support/**', 46 | 'app/**', 47 | 'tests/dummy/app/**', 48 | ], 49 | parserOptions: { 50 | sourceType: 'script', 51 | }, 52 | env: { 53 | browser: false, 54 | node: true, 55 | }, 56 | plugins: ['node'], 57 | extends: ['plugin:node/recommended'], 58 | }, 59 | ], 60 | }; 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist/ 5 | /tmp/ 6 | 7 | # dependencies 8 | /node_modules 9 | 10 | # misc 11 | /.env* 12 | /.pnp* 13 | /.sass-cache 14 | /.eslintcache 15 | /connect.lock 16 | /coverage/ 17 | /libpeerconnection.log 18 | npm-debug.log* 19 | yarn-error.log 20 | testem.log 21 | /typings 22 | 23 | # ember-try 24 | .node_modules.ember-try/ 25 | package.json.ember-try 26 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist/ 3 | /tmp/ 4 | 5 | # dependencies 6 | /bower_components/ 7 | 8 | # misc 9 | /.bowerrc 10 | /.editorconfig 11 | /.ember-cli 12 | /.env* 13 | /.eslintcache 14 | /.eslintignore 15 | /.eslintrc.js 16 | /.git/ 17 | /.gitignore 18 | /.prettierignore 19 | /.prettierrc.js 20 | /.template-lintrc.js 21 | /.travis.yml 22 | /.watchmanconfig 23 | /bower.json 24 | /config/ember-try.js 25 | /CONTRIBUTING.md 26 | /ember-cli-build.js 27 | /testem.js 28 | /tests/ 29 | /yarn.lock 30 | .gitkeep 31 | 32 | # ember-try 33 | /.node_modules.ember-try/ 34 | /bower.json.ember-try 35 | /package.json.ember-try 36 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | !.* 16 | .eslintcache 17 | 18 | # ember-try 19 | /.node_modules.ember-try/ 20 | /bower.json.ember-try 21 | /package.json.ember-try 22 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | singleQuote: true, 5 | }; 6 | -------------------------------------------------------------------------------- /.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | // extends: 'octane', 5 | extends: 'recommended', 6 | }; 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | node_js: 4 | # we recommend testing addons with the same minimum supported node version as Ember CLI 5 | # so that your addon works for all apps 6 | - "12" 7 | 8 | dist: xenial 9 | 10 | addons: 11 | chrome: stable 12 | 13 | cache: 14 | directories: 15 | - $HOME/.npm 16 | 17 | env: 18 | global: 19 | - $HOME/.cache # includes bowers cache 20 | # See https://git.io/vdao3 for details. 21 | - JOBS=1 22 | 23 | branches: 24 | only: 25 | - master 26 | # npm version tags 27 | - /^v\d+\.\d+\.\d+/ 28 | 29 | jobs: 30 | fast_finish: true 31 | allow_failures: 32 | - env: EMBER_TRY_SCENARIO=ember-canary 33 | 34 | include: 35 | # runs linting and tests with current locked deps 36 | - stage: "Tests" 37 | name: "Tests" 38 | script: 39 | - npm run lint 40 | - npm run test:ember 41 | 42 | - stage: "Additional Tests" 43 | name: "Floating Dependencies" 44 | install: 45 | - npm install --no-package-lock 46 | script: 47 | - npm run test:ember 48 | 49 | # we recommend new addons test the current and previous LTS 50 | # as well as latest stable release (bonus points to beta/canary) 51 | - env: EMBER_TRY_SCENARIO=ember-lts-3.16 52 | - env: EMBER_TRY_SCENARIO=ember-lts-3.20 53 | - env: EMBER_TRY_SCENARIO=ember-release 54 | - env: EMBER_TRY_SCENARIO=ember-beta 55 | - env: EMBER_TRY_SCENARIO=ember-canary 56 | - env: EMBER_TRY_SCENARIO=ember-default-with-jquery 57 | - env: EMBER_TRY_SCENARIO=ember-classic 58 | - env: EMBER_TRY_SCENARIO=embroider-safe 59 | - env: EMBER_TRY_SCENARIO=embroider-optimized 60 | 61 | install: 62 | - npm install 63 | 64 | script: 65 | # Only enable coverage if scenario is ember-release, since we only want to do it once 66 | - COVERAGE=false && [[ "$EMBER_TRY_SCENARIO" == "ember-release" ]] && COVERAGE=true 67 | - npm run lint:js 68 | # Usually, it's ok to finish the test scenario without reverting 69 | # to the addon's original dependency state, skipping "cleanup". 70 | - COVERAGE=$COVERAGE node_modules/.bin/ember try:one $EMBER_TRY_SCENARIO --skip-cleanup 71 | 72 | after_script: 73 | - if [ -f ./coverage/lcov.info ]; then cat ./coverage/lcov.info | node_modules/.bin/coveralls && rm -rf ./coverage; fi 74 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [5.0.0](https://github.com/knownasilya/google-maps-markup/compare/v4.0.0...v5.0.0) (2021-06-08) 6 | 7 | 8 | ### ⚠ BREAKING CHANGES 9 | 10 | * internal components renamed, which might have been used by consumers directly. 11 | * drop Node.js < 12 12 | 13 | ### Bug Fixes 14 | 15 | * changing mode not changing results ([2b33f28](https://github.com/knownasilya/google-maps-markup/commit/2b33f2848d58a3d36e80f678bbeb29de661ac5c5)) 16 | * convert results component to glimmer component ([ab18a13](https://github.com/knownasilya/google-maps-markup/commit/ab18a13bfd7fbbd68239b79e24ade9a91206a0a4)) 17 | * convert tools to gc ([041b80d](https://github.com/knownasilya/google-maps-markup/commit/041b80d4e5893d7f585eb1791f9fb1b6469300b3)) 18 | * general fixes around editable, popups, and rendering items ([ba25220](https://github.com/knownasilya/google-maps-markup/commit/ba252205c0b34959f9c926b0b1e4b09125ab506f)) 19 | * map events setup ([12d6862](https://github.com/knownasilya/google-maps-markup/commit/12d68622d9f87d000871992fa560b287f5e240a4)) 20 | * parent/child registration (hover on geom highlights ui) ([97af4f2](https://github.com/knownasilya/google-maps-markup/commit/97af4f24d4f71442630008b809c59dc594557836)), closes [/github.com/miguelcobain/ember-composability-tools/issues/47#issuecomment-835463333](https://github.com/knownasilya//github.com/miguelcobain/ember-composability-tools/issues/47/issues/issuecomment-835463333) 21 | * rename internal components ([a49429e](https://github.com/knownasilya/google-maps-markup/commit/a49429e9ee09e47353ad6615adc58422260d2866)) 22 | * update deps, and drop node < 12 ([fb7ef16](https://github.com/knownasilya/google-maps-markup/commit/fb7ef16dd31536594dcd3e63fd795783089c9f4f)) 23 | * update setup on map load, and remove jquery ([713482d](https://github.com/knownasilya/google-maps-markup/commit/713482d636289ac9fdfe2151a16151f6db44316a)) 24 | * update the rest to GC and use new composability tools ([73e5640](https://github.com/knownasilya/google-maps-markup/commit/73e5640cb45b0a908f0beec017495c5f620d12f0)) 25 | 26 | ## [4.0.0](https://github.com/knownasilya/google-maps-markup/compare/v3.2.5...v4.0.0) (2021-05-07) 27 | 28 | 29 | ### ⚠ BREAKING CHANGES 30 | 31 | * drop Ember < 3.16 32 | 33 | ### Bug Fixes 34 | 35 | * embroider maybe test ([c03f109](https://github.com/knownasilya/google-maps-markup/commit/c03f109448adbfd1344a66137776e2c831a5b7d5)) 36 | * v3.17.0...v3.26.1 ([260785e](https://github.com/knownasilya/google-maps-markup/commit/260785ec415b7d4d0ae22b13d3ee686ae0df2b3e)) 37 | 38 | ### [3.2.5](https://github.com/knownasilya/google-maps-markup/compare/v3.2.4...v3.2.5) (2021-04-07) 39 | 40 | 41 | ### Bug Fixes 42 | 43 | * update dependencies ([b2d1683](https://github.com/knownasilya/google-maps-markup/commit/b2d1683d7d49be4abe603680fcd645f6366d26cd)) 44 | 45 | ### [3.2.4](https://github.com/knownasilya/google-maps-markup/compare/v3.2.3...v3.2.4) (2020-09-30) 46 | 47 | 48 | ### Bug Fixes 49 | 50 | * tools using google before init ([041b60c](https://github.com/knownasilya/google-maps-markup/commit/041b60c27c614872d3889f0e1ead8e12da8aa44f)) 51 | 52 | ### [3.2.3](https://github.com/knownasilya/google-maps-markup/compare/v3.2.2...v3.2.3) (2020-09-24) 53 | 54 | 55 | ### Bug Fixes 56 | 57 | * setup the options so edit works on import, also fix layer interactions not working after changing modes ([2e4a188](https://github.com/knownasilya/google-maps-markup/commit/2e4a18885155376c30f58cc436cac30f039844ce)) 58 | 59 | ### [3.2.2](https://github.com/knownasilya/google-maps-markup/compare/v3.2.1...v3.2.2) (2020-09-24) 60 | 61 | 62 | ### Bug Fixes 63 | 64 | * class syntax a bit and preserve editability on load ([#45](https://github.com/knownasilya/google-maps-markup/issues/45)) ([182f7ad](https://github.com/knownasilya/google-maps-markup/commit/182f7adcedef7167cc331a7f34bf7b78eea5c2c1)) 65 | 66 | ### [3.2.1](https://github.com/knownasilya/google-maps-markup/compare/v3.2.0...v3.2.1) (2020-09-17) 67 | 68 | 69 | ### Bug Fixes 70 | 71 | * update dnd library ([5d49f92](https://github.com/knownasilya/google-maps-markup/commit/5d49f92d5714c9b27c8c178966321a72f70594e9)) 72 | 73 | ## [3.2.0](https://github.com/knownasilya/google-maps-markup/compare/v3.1.0...v3.2.0) (2020-09-17) 74 | 75 | 76 | ### Features 77 | 78 | * fill opacity ([7e7cd45](https://github.com/knownasilya/google-maps-markup/commit/7e7cd454a6439b978f9d0632c1b6abe68823a104)) 79 | 80 | 81 | ### Bug Fixes 82 | 83 | * opacity not setting untill mouse out, make options ui a grid, over only over header now ([c09adcd](https://github.com/knownasilya/google-maps-markup/commit/c09adcd1b710fa17e4af786493359d59db5894a0)) 84 | 85 | ## [3.1.0](https://github.com/knownasilya/google-maps-markup/compare/v3.0.4...v3.1.0) (2020-09-17) 86 | 87 | 88 | ### Features 89 | 90 | * text highlight on mouseover ([a9d6635](https://github.com/knownasilya/google-maps-markup/commit/a9d66357546f497d220bb4c36907c1926a456999)) 91 | 92 | 93 | ### Bug Fixes 94 | 95 | * hovering over items if text present on map ([9cc549b](https://github.com/knownasilya/google-maps-markup/commit/9cc549bde7ceb94222c8c266e9de6f589c44b1e6)) 96 | * remove scrollbar from tall text ([7330c7e](https://github.com/knownasilya/google-maps-markup/commit/7330c7ee410e0be89f118d7456743249250ef39a)) 97 | 98 | ### [3.0.4](https://github.com/knownasilya/google-maps-markup/compare/v3.0.3...v3.0.4) (2020-09-16) 99 | 100 | 101 | ### Bug Fixes 102 | 103 | * clicking edit on items throwing error for text features ([69a2a72](https://github.com/knownasilya/google-maps-markup/commit/69a2a7238def59873771825cdc627d8cf30aecfa)) 104 | * codemods ([33e89da](https://github.com/knownasilya/google-maps-markup/commit/33e89dacdff20c3122290d0c81be4fa885df6101)) 105 | * no imlicit this ([c987c61](https://github.com/knownasilya/google-maps-markup/commit/c987c61dd4df79164a2337753d76768cebb3522d)) 106 | * not being able to place text on measured shapes ([61420a7](https://github.com/knownasilya/google-maps-markup/commit/61420a709035798a12804520f8104390c7690f69)) 107 | * plotter computeArea being undefined ([2f6c465](https://github.com/knownasilya/google-maps-markup/commit/2f6c465da2610c0c74f5e90dbaca157c496f140d)) 108 | 109 | 110 | ## [3.0.3](https://github.com/knownasilya/google-maps-markup/compare/v3.0.2...v3.0.3) (2020-05-13) 111 | 112 | 113 | ### Bug Fixes 114 | 115 | * default export map label ([545bb74](https://github.com/knownasilya/google-maps-markup/commit/545bb74)) 116 | 117 | 118 | 119 | 120 | ## [3.0.2](https://github.com/knownasilya/google-maps-markup/compare/v3.0.1...v3.0.2) (2020-05-11) 121 | 122 | 123 | ### Bug Fixes 124 | 125 | * dont use google in module scope ([222f511](https://github.com/knownasilya/google-maps-markup/commit/222f511)) 126 | 127 | 128 | 129 | 130 | ## [3.0.1](https://github.com/knownasilya/google-maps-markup/compare/v3.0.0...v3.0.1) (2020-04-29) 131 | 132 | 133 | ### Bug Fixes 134 | 135 | * use ember-copy and loosen up deps ([11cda0f](https://github.com/knownasilya/google-maps-markup/commit/11cda0f)) 136 | 137 | 138 | 139 | 140 | # [3.0.0](https://github.com/knownasilya/google-maps-markup/compare/v2.2.13...v3.0.0) (2020-04-29) 141 | 142 | 143 | ### Bug Fixes 144 | 145 | * update changes from auto update ([5fd2947](https://github.com/knownasilya/google-maps-markup/commit/5fd2947)) 146 | * update composability tools ([d0b1507](https://github.com/knownasilya/google-maps-markup/commit/d0b1507)) 147 | * v3.1.4...v3.17. ([de287e6](https://github.com/knownasilya/google-maps-markup/commit/de287e6)) 148 | 149 | 150 | ### BREAKING CHANGES 151 | 152 | * Drop old node, ember-cli, and ember support. See compatibility section in readme. 153 | 154 | 155 | 156 | 157 | ## [2.2.13](https://github.com/knownasilya/google-maps-markup/compare/v2.2.12...v2.2.13) (2019-05-01) 158 | 159 | 160 | ### Bug Fixes 161 | 162 | * remove bower ([8f1077a](https://github.com/knownasilya/google-maps-markup/commit/8f1077a)) 163 | * update clear all message, resolves [#34](https://github.com/knownasilya/google-maps-markup/issues/34) ([6dedcd1](https://github.com/knownasilya/google-maps-markup/commit/6dedcd1)) 164 | * update ghpages dep ([6a03ac3](https://github.com/knownasilya/google-maps-markup/commit/6a03ac3)) 165 | 166 | 167 | 168 | 169 | ## [2.2.12](https://github.com/knownasilya/google-maps-markup/compare/v2.2.11...v2.2.12) (2018-10-01) 170 | 171 | 172 | ### Bug Fixes 173 | 174 | * update power select version ([0dcb421](https://github.com/knownasilya/google-maps-markup/commit/0dcb421)) 175 | 176 | 177 | 178 | 179 | ## [2.2.11](https://github.com/knownasilya/google-maps-markup/compare/v2.2.10...v2.2.11) (2018-07-17) 180 | 181 | 182 | ### Bug Fixes 183 | 184 | * auto unit change for distance, format to own module ([a0edf9a](https://github.com/knownasilya/google-maps-markup/commit/a0edf9a)) 185 | 186 | 187 | 188 | 189 | ## [2.2.10](https://github.com/knownasilya/google-maps-markup/compare/v2.2.9...v2.2.10) (2018-07-17) 190 | 191 | 192 | ### Bug Fixes 193 | 194 | * area unit assumptions and usage ([7d22133](https://github.com/knownasilya/google-maps-markup/commit/7d22133)) 195 | 196 | 197 | 198 | 199 | ## [2.2.9](https://github.com/knownasilya/google-maps-markup/compare/v2.2.8...v2.2.9) (2018-07-16) 200 | 201 | 202 | ### Bug Fixes 203 | 204 | * area auto next unit if above a threshold ([429ac37](https://github.com/knownasilya/google-maps-markup/commit/429ac37)) 205 | * remove non existent references to a util ([eabdf0e](https://github.com/knownasilya/google-maps-markup/commit/eabdf0e)) 206 | 207 | 208 | 209 | 210 | ## [2.2.8](https://github.com/knownasilya/google-maps-markup/compare/v2.2.7...v2.2.8) (2018-06-01) 211 | 212 | 213 | ### Bug Fixes 214 | 215 | * clean up deprecations and linting ([dff2d76](https://github.com/knownasilya/google-maps-markup/commit/dff2d76)) 216 | * update all the things ([735cfec](https://github.com/knownasilya/google-maps-markup/commit/735cfec)) 217 | * update the deps, add load and save to dummy app ([fc7c903](https://github.com/knownasilya/google-maps-markup/commit/fc7c903)) 218 | 219 | 220 | 221 | 222 | ## [2.2.7](https://github.com/knownasilya/google-maps-markup/compare/v2.2.6...v2.2.7) (2018-03-21) 223 | 224 | 225 | ### Bug Fixes 226 | 227 | * **service:** move distanceUnitId to result from feature ([217066b](https://github.com/knownasilya/google-maps-markup/commit/217066b)) 228 | * save distanceUnitId on feature for markup ([3209a1a](https://github.com/knownasilya/google-maps-markup/commit/3209a1a)) 229 | 230 | 231 | 232 | 233 | ## [2.2.6](https://github.com/knownasilya/google-maps-markup/compare/v2.2.5...v2.2.6) (2017-12-18) 234 | 235 | 236 | ### Bug Fixes 237 | 238 | * distanceUnitId being overwritten on click ([f567a26](https://github.com/knownasilya/google-maps-markup/commit/f567a26)) 239 | 240 | 241 | 242 | 243 | ## [2.2.5](https://github.com/knownasilya/google-maps-markup/compare/v2.2.4...v2.2.5) (2017-11-16) 244 | 245 | 246 | ### Bug Fixes 247 | 248 | * enable crosshair for freeform poly tool ([8913108](https://github.com/knownasilya/google-maps-markup/commit/8913108)) 249 | * path distance incorrectly setting the wrong var ([b90989a](https://github.com/knownasilya/google-maps-markup/commit/b90989a)) 250 | 251 | 252 | 253 | 254 | ## [2.2.4](https://github.com/knownasilya/google-maps-markup/compare/v2.2.3...v2.2.4) (2017-10-18) 255 | 256 | 257 | ### Bug Fixes 258 | 259 | * option layout columns ([01dce1a](https://github.com/knownasilya/google-maps-markup/commit/01dce1a)) 260 | 261 | 262 | 263 | 264 | ## [2.2.3](https://github.com/knownasilya/google-maps-markup/compare/v2.2.2...v2.2.3) (2017-10-18) 265 | 266 | 267 | ### Bug Fixes 268 | 269 | * name not set when using featureToResult in service ([15d0e17](https://github.com/knownasilya/google-maps-markup/commit/15d0e17)) 270 | 271 | 272 | 273 | 274 | ## [2.2.2](https://github.com/knownasilya/google-maps-markup/compare/v2.2.1...v2.2.2) (2017-10-18) 275 | 276 | 277 | ### Bug Fixes 278 | 279 | * add name to feature for saving ([9e4e304](https://github.com/knownasilya/google-maps-markup/commit/9e4e304)) 280 | 281 | 282 | 283 | 284 | ## [2.2.1](https://github.com/knownasilya/google-maps-markup/compare/v2.2.0...v2.2.1) (2017-10-18) 285 | 286 | 287 | ### Bug Fixes 288 | 289 | * remove unused helper, add tool-id test ([b9bb7f3](https://github.com/knownasilya/google-maps-markup/commit/b9bb7f3)) 290 | * use getTool on click, prevents error ([312d776](https://github.com/knownasilya/google-maps-markup/commit/312d776)) 291 | * use reduce to calc value for path distance ([4ba8e7b](https://github.com/knownasilya/google-maps-markup/commit/4ba8e7b)) 292 | 293 | 294 | 295 | 296 | # [2.2.0](https://github.com/knownasilya/google-maps-markup/compare/v2.1.3...v2.2.0) (2017-10-16) 297 | 298 | 299 | ### Bug Fixes 300 | 301 | * add back dependencies missed during cli upgrade ([401ea0a](https://github.com/knownasilya/google-maps-markup/commit/401ea0a)) 302 | * make edit mode exclusive ([1bf8fb2](https://github.com/knownasilya/google-maps-markup/commit/1bf8fb2)) 303 | * result item hover on the full draggable element ([931fd68](https://github.com/knownasilya/google-maps-markup/commit/931fd68)) 304 | 305 | 306 | ### Features 307 | 308 | * hover markup on map mouse interaction ([0fb9ec3](https://github.com/knownasilya/google-maps-markup/commit/0fb9ec3)) 309 | 310 | 311 | 312 | 313 | ## [2.1.3](https://github.com/knownasilya/google-maps-markup/compare/v2.1.2...v2.1.3) (2017-10-16) 314 | 315 | 316 | ### Bug Fixes 317 | 318 | * missing event in dblclick handler for measurement plotter ([cf9d549](https://github.com/knownasilya/google-maps-markup/commit/cf9d549)) 319 | 320 | 321 | 322 | 323 | ## [2.1.2](https://github.com/knownasilya/google-maps-markup/compare/v2.1.1...v2.1.2) (2017-10-16) 324 | 325 | 326 | ### Bug Fixes 327 | 328 | * freeform activating on of the dm tools if those clicked before hand ([f5483b1](https://github.com/knownasilya/google-maps-markup/commit/f5483b1)) 329 | 330 | 331 | 332 | 333 | ## [2.1.1](https://github.com/knownasilya/google-maps-markup/compare/v2.1.0...v2.1.1) (2017-10-16) 334 | 335 | 336 | ### Bug Fixes 337 | 338 | * freeform polygon keeping path drawing around after finishing shape ([34b07ab](https://github.com/knownasilya/google-maps-markup/commit/34b07ab)) 339 | 340 | 341 | 342 | 343 | # [2.1.0](https://github.com/knownasilya/google-maps-markup/compare/v2.0.3...v2.1.0) (2017-10-15) 344 | 345 | 346 | ### Bug Fixes 347 | 348 | * add autoreset to freeform poly and change tool (to text as well) ([e24a6f3](https://github.com/knownasilya/google-maps-markup/commit/e24a6f3)) 349 | * add eps dep explicitly, styles and remove old stuff ([7d54b58](https://github.com/knownasilya/google-maps-markup/commit/7d54b58)) 350 | * build issue fixed ([#26](https://github.com/knownasilya/google-maps-markup/issues/26)) ([f69bc31](https://github.com/knownasilya/google-maps-markup/commit/f69bc31)) 351 | * Can only enter one text character ([#27](https://github.com/knownasilya/google-maps-markup/issues/27)) ([17d50f1](https://github.com/knownasilya/google-maps-markup/commit/17d50f1)) 352 | * certain styles leaking into others ([77a546f](https://github.com/knownasilya/google-maps-markup/commit/77a546f)) 353 | * Circle or Rectangle on the map is getting draw without fill color ([#24](https://github.com/knownasilya/google-maps-markup/issues/24)) ([5c00d8f](https://github.com/knownasilya/google-maps-markup/commit/5c00d8f)) 354 | * drag and drop ui layout ([52368fc](https://github.com/knownasilya/google-maps-markup/commit/52368fc)) 355 | * dynamic-label error when removing ([78155d3](https://github.com/knownasilya/google-maps-markup/commit/78155d3)) 356 | * edit allowed by default, editable option only enables shape editing ([4127a5a](https://github.com/knownasilya/google-maps-markup/commit/4127a5a)) 357 | * freeform drawing with mobile ([d83256f](https://github.com/knownasilya/google-maps-markup/commit/d83256f)) 358 | * freeform polygon transparent by and styles leaking ([354c68e](https://github.com/knownasilya/google-maps-markup/commit/354c68e)) 359 | * infowindow popup during edit ([7caa87e](https://github.com/knownasilya/google-maps-markup/commit/7caa87e)) 360 | * make fill color checkbox clickable with label ([1ce1529](https://github.com/knownasilya/google-maps-markup/commit/1ce1529)) 361 | * make markers not editable for now ([e33297f](https://github.com/knownasilya/google-maps-markup/commit/e33297f)) 362 | * marker hover/zindex change ([59ce53d](https://github.com/knownasilya/google-maps-markup/commit/59ce53d)) 363 | * modules codemod ran ([a5ca8de](https://github.com/knownasilya/google-maps-markup/commit/a5ca8de)) 364 | * move data to another file ([d6ee138](https://github.com/knownasilya/google-maps-markup/commit/d6ee138)) 365 | * **deps:** remove unused bower deps, update merge trees ([be946ab](https://github.com/knownasilya/google-maps-markup/commit/be946ab)) 366 | * normalize options into a component ([a43960b](https://github.com/knownasilya/google-maps-markup/commit/a43960b)) 367 | * options layout and rectangle missing stroke weight ([55d29fc](https://github.com/knownasilya/google-maps-markup/commit/55d29fc)) 368 | * options size on small screen ([3422463](https://github.com/knownasilya/google-maps-markup/commit/3422463)) 369 | * text fontsize changing and position during zoom and sizing ([98c35c3](https://github.com/knownasilya/google-maps-markup/commit/98c35c3)) 370 | * unit issues and how units are saved. Remove data from tools ([d426d81](https://github.com/knownasilya/google-maps-markup/commit/d426d81)) 371 | * update deps and fix text label in items list ([b72a5de](https://github.com/knownasilya/google-maps-markup/commit/b72a5de)) 372 | * update linting, remove unused jshint config ([bd515d3](https://github.com/knownasilya/google-maps-markup/commit/bd515d3)) 373 | * Update to ember-cli 2.16 ([2563baf](https://github.com/knownasilya/google-maps-markup/commit/2563baf)) 374 | 375 | 376 | ### Features 377 | 378 | * Ability to change line width of markup/measurement ([#22](https://github.com/knownasilya/google-maps-markup/issues/22)) ([3090d60](https://github.com/knownasilya/google-maps-markup/commit/3090d60)) 379 | * Add option to change font size for text ([#23](https://github.com/knownasilya/google-maps-markup/issues/23)) ([32e7890](https://github.com/knownasilya/google-maps-markup/commit/32e7890)) 380 | * Allow reorder markup in the list ([#20](https://github.com/knownasilya/google-maps-markup/issues/20)) ([e9c5105](https://github.com/knownasilya/google-maps-markup/commit/e9c5105)) 381 | * bring forward to the top any markup when you hover over it in the list ([#21](https://github.com/knownasilya/google-maps-markup/issues/21)) ([4d81b98](https://github.com/knownasilya/google-maps-markup/commit/4d81b98)) 382 | * Create a choice of measurement units ([#19](https://github.com/knownasilya/google-maps-markup/issues/19)) ([dc2bdc0](https://github.com/knownasilya/google-maps-markup/commit/dc2bdc0)) 383 | * Individual markups editing (colors, opacity) ([#25](https://github.com/knownasilya/google-maps-markup/issues/25)) ([28b28ca](https://github.com/knownasilya/google-maps-markup/commit/28b28ca)) 384 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How To Contribute 2 | 3 | ## Installation 4 | 5 | * `git clone ` 6 | * `cd google-maps-markup` 7 | * `npm install` 8 | 9 | ## Linting 10 | 11 | * `npm run lint` 12 | * `npm run lint:fix` 13 | 14 | ## Running tests 15 | 16 | * `ember test` – Runs the test suite on the current Ember version 17 | * `ember test --server` – Runs the test suite in "watch mode" 18 | * `ember try:each` – Runs the test suite against multiple Ember versions 19 | 20 | ## Running the dummy application 21 | 22 | * `ember serve` 23 | * Visit the dummy application at [http://localhost:4200](http://localhost:4200). 24 | 25 | For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/). 26 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # google-maps-markup 2 | 3 | Drawing and measurement tools for a Google Map 4 | 5 | [![npm version](https://badge.fury.io/js/google-maps-markup.svg)](http://badge.fury.io/js/google-maps-markup) 6 | [![Build Status](https://travis-ci.org/knownasilya/google-maps-markup.svg)](https://travis-ci.org/knownasilya/google-maps-markup) 7 | [![Coverage Status](https://coveralls.io/repos/knownasilya/google-maps-markup/badge.svg?branch=master&service=github)](https://coveralls.io/github/knownasilya/google-maps-markup?branch=master) 8 | [![Ember Observer Score](http://emberobserver.com/badges/google-maps-markup.svg)](http://emberobserver.com/addons/google-maps-markup) 9 | 10 | [Preview Demo] 11 | 12 | ## Compatibility 13 | * Ember.js v3.16 or above 14 | * Ember CLI v2.13 or above 15 | * Node.js v10 or above 16 | 17 | ## Usage 18 | 19 | ```bash 20 | ember install google-maps-markup 21 | ``` 22 | 23 | ```hbs 24 | 25 | ``` 26 | 27 | ### Available Attributes 28 | 29 | - `map` - **REQUIRED**; Google Map instance, defaults to `undefined`. Bring your own map! 30 | - `editable` - (experimental) Allow shapes to be edited. Defaults to `false`. 31 | - `panForOffscreen` - On hover pan to shape if not in view (reset to last bounds after). Defaults to `true`. 32 | - `autoResetToPan` - After drawing a shape the tool changes to "Pan" instead of staying on the current tool. Defaults to `false`. 33 | 34 | #### Actions 35 | 36 | - `afterAddFeature` - Fires after finishing some markup on the map. Passes the result as the first argument, i.e. `afterAddFeature(result) {}`. 37 | - `afterClearResults` - Fires after clicking "Clear" for a mode. Passes the mode as the first argument, i.e `afterClearResults(mode) {}`. 38 | 39 | ### Service 40 | 41 | The service is called `markupData` and allows access to the result data that gets created when you 42 | create markup on the map. It also has some helper functions. 43 | 44 | ```js 45 | import Component from '@glimmer/component'; 46 | 47 | export default class MyComponent extends Component { 48 | @service('markup-data') 49 | markupDataService; 50 | } 51 | ``` 52 | 53 | #### Properties 54 | 55 | - `layers` - Array of Google Maps Data layers, one for draw and one for measure. 56 | - `results` - Markup data for each markup you create, based on mode. See `markupResults` for all results. 57 | - `markupResults` - The object of all the results, no matter the mode. 58 | - `mode` - The drawing mode, either 'draw' or 'measure'. 59 | 60 | #### Methods 61 | 62 | - `activate` - Add all layers to the map. `activate(map)`. 63 | - `changeModeByResults` - Changes the mode to the first layer with results. 64 | - `featureToResult` - Converts a Google Maps Data Feature to a markup result, for loading data without 65 | actually drawing on the map (ie, load via url). `featureToResult(feature, layer)`. 66 | 67 | ## Installation 68 | 69 | - `git clone` this repository 70 | - `yarn` 71 | 72 | ### Linting 73 | 74 | - `npm run lint:js` 75 | - `npm run lint:js -- --fix` 76 | 77 | ### Running tests 78 | 79 | - `ember test` – Runs the test suite on the current Ember version 80 | - `ember test --server` – Runs the test suite in "watch mode" 81 | - `ember try:each` – Runs the test suite against multiple Ember versions 82 | 83 | ### Running the dummy application 84 | 85 | - `ember serve` 86 | - Visit the dummy application at [http://localhost:4200](http://localhost:4200). 87 | 88 | For developing locally with your app, you can use `DEVELOPING=true npm start` for your app, and 89 | `npm link path/to/this/addon` and your app will automatically rebuild as you make changes to your 90 | local version of this addon. 91 | 92 | For more information on using ember-cli, visit [http://www.ember-cli.com/](http://www.ember-cli.com/). 93 | 94 | ## Github Pages/Demo 95 | 96 | Build by checking out the relevant branch, since the test dummy app 97 | is actually the demo app. 98 | 99 | Run the following command: 100 | 101 | ```no-highlight 102 | ember github-pages:commit --message 103 | ``` 104 | 105 | For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/). 106 | 107 | ## Contributing 108 | 109 | See the [Contributing](CONTRIBUTING.md) guide for details. 110 | 111 | ## License 112 | 113 | This project is licensed under the [MIT License](LICENSE.md). 114 | 115 | [preview demo]: http://knownasilya.github.io/google-maps-markup 116 | -------------------------------------------------------------------------------- /addon/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knownasilya/google-maps-markup/27861c2eebe34bb73e91ce4ff45657576ac5d2d1/addon/.gitkeep -------------------------------------------------------------------------------- /addon/components/gmm-result-item.hbs: -------------------------------------------------------------------------------- 1 | {{! template-lint-disable no-invalid-interactive}} 2 |
8 | 9 | {{@data.name}} 10 | 11 | 12 | {{if this.description ':'}} 13 | {{this.description}} 14 | 15 | 22 | 23 |
24 | {{#if @data.editing}} 25 | 32 | {{else if @data.isEditable}} 33 | 40 | {{/if}} 41 | 42 | 49 |
50 |
51 | 52 | {{#if @data.editing}} 53 | {{#if @editable}} 54 | {{#if @popupElement}} 55 | 56 | {{#if this.shapeModified}} 57 | 64 | {{/if}} 65 | 66 | 73 | 74 | {{/if}} 75 | {{/if}} 76 | 77 | {{#if @data.isEditable}} 78 | {{#if @data.options}} 79 | 83 | {{/if}} 84 | {{/if}} 85 | {{/if}} -------------------------------------------------------------------------------- /addon/components/gmm-result-item.js: -------------------------------------------------------------------------------- 1 | import { guidFor } from '@ember/object/internals'; 2 | import { tracked } from '@glimmer/tracking'; 3 | import { set, action, computed } from '@ember/object'; 4 | import { Node } from 'ember-composability-tools'; 5 | import MODE from '../utils/modes'; 6 | import getMeasurement from '../utils/get-measurement'; 7 | import featureCenter from '../utils/feature-center'; 8 | 9 | export default class GmmResultItem extends Node { 10 | guid = guidFor(this); 11 | @tracked textLabel; 12 | 13 | constructor() { 14 | super(...arguments); 15 | 16 | let data = this.args.data; 17 | 18 | if (data.feature.addListener) { 19 | let changeListener = data.feature.addListener('changelabel', () => { 20 | data.geojson.properties.label = data.feature.label; 21 | this.textLabel = data.feature.label; 22 | }); 23 | 24 | this.changeListener = changeListener; 25 | } 26 | } 27 | 28 | @computed('data.{mode,feature}', 'textLabel') 29 | get description() { 30 | if (this.textLabel) { 31 | return this.textLabel; 32 | } 33 | 34 | let data = this.args.data; 35 | let mode = data.mode; 36 | 37 | if (mode === MODE.measure.id) { 38 | let m = getMeasurement(data.type, data.feature, data.distanceUnitId); 39 | // update the measure label 40 | data.label.label = `${m.value} ${m.unit.display}`; 41 | 42 | return `${m.measurementType}: ${m.value} ${m.unit.display}`; 43 | } else if (data.type === 'text') { 44 | return data.feature.label; 45 | } 46 | 47 | return ''; 48 | } 49 | 50 | @action 51 | ok() { 52 | let data = this.args.data; 53 | 54 | set(data, 'editing', false); 55 | } 56 | 57 | @action 58 | updateOptionValue(tool, prop, value) { 59 | let data = this.args.data; 60 | 61 | if (tool.type === 'text') { 62 | let [type, specific] = prop.split('.'); 63 | 64 | if (type && type === 'style' && specific) { 65 | data.feature[specific] = value; 66 | } 67 | 68 | set(tool, prop, value); 69 | } else { 70 | set(tool, prop, value); 71 | 72 | data.layer.data.overrideStyle(data.feature, data.style); 73 | } 74 | } 75 | 76 | @action 77 | toggleEditShape() { 78 | let data = this.args.data; 79 | let edit = !data.editingShape; 80 | let listener; 81 | 82 | set(data, 'editingShape', edit); 83 | 84 | if (edit) { 85 | if (data.type === 'text') { 86 | this.originalFeatureGeometry = data.feature.position; 87 | data.feature.draggable = true; 88 | // TODO: implement label dragging 89 | } else { 90 | listener = google.maps.event.addListener( 91 | data.feature, 92 | 'setgeometry', 93 | () => { 94 | if (data.label) { 95 | data.label.position = featureCenter(data.feature); 96 | } 97 | // force recalculation 98 | this.shapeModified = true; 99 | // this.notifyPropertyChange('description'); 100 | } 101 | ); 102 | this.originalFeatureGeometry = data.feature.getGeometry(); 103 | data.layer.data.overrideStyle(data.feature, { 104 | editable: true, 105 | draggable: true, 106 | }); 107 | } 108 | } else { 109 | if (data.type === 'text') { 110 | // TODO: implement 111 | } else { 112 | data.layer.data.revertStyle(data.feature); 113 | if (data.style) { 114 | data.layer.data.overrideStyle(data.feature, data.style); 115 | } 116 | this.shapeModified = false; 117 | if (listener) { 118 | google.maps.event.removeListener(listener); 119 | } 120 | } 121 | } 122 | 123 | this.args.data = data; 124 | } 125 | 126 | @action 127 | cancelEditShape() { 128 | let data = this.args.data; 129 | let shapeModified = this.shapeModified; 130 | let originalGeometry = this.originalFeatureGeometry; 131 | 132 | if (shapeModified && originalGeometry) { 133 | this.originalFeatureGeometry = undefined; 134 | this.toggleEditShape(); 135 | data.feature.setGeometry(originalGeometry); 136 | this.shapeModified = false; 137 | } 138 | } 139 | 140 | @action 141 | beforeRemove() { 142 | let changeListener = this.changeListener; 143 | 144 | if (changeListener) { 145 | google.maps.event.removeListener(changeListener); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /addon/components/gmm-sortable-results.hbs: -------------------------------------------------------------------------------- 1 | {{#if @results}} 2 |

3 | Results 4 |

5 |
6 | 14 | 22 |
23 | 24 | 30 | {{#each @results as |result|}} 31 | 38 | {{yield result}} 39 | 40 | {{/each}} 41 | 42 | {{/if}} -------------------------------------------------------------------------------- /addon/components/gmm-sortable-results.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import { action } from '@ember/object'; 3 | 4 | export default class GmmSortableResults extends Component { 5 | @action 6 | sortEndAction() { 7 | let results = this.args.results; 8 | 9 | results.forEach((result) => { 10 | result.style.zIndex = 0; 11 | }); 12 | results[0].style.zIndex = 111; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /addon/components/gmm-tool-options.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{#each @tool.options as |opt|}} 3 | {{#if (is-equal opt.type 'color')}} 4 |
5 | 8 | 12 |
13 | {{else if (is-equal opt.type 'opacity')}} 14 |
15 | 18 | 26 |
27 | {{else if (is-equal opt.type 'width')}} 28 |
29 | 32 | 41 | {{width}} 42 | 43 |
44 | {{else if (is-equal opt.type 'size')}} 45 |
46 |
47 | 50 |
51 | 52 | 61 | {{size}} 62 | 63 |
64 | {{else if (is-equal @mode 'measure')}} 65 | {{#if (is-equal opt.type 'distanceUnit')}} 66 |
67 |
68 | 71 |
72 | 81 | 82 | {{unit.display}} 83 | 84 | 85 |
86 | {{/if}} 87 | {{/if}} 88 | {{/each}} 89 |
-------------------------------------------------------------------------------- /addon/components/gmm-tool-options.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import { action } from '@ember/object'; 3 | import optionsData from '../utils/options-data'; 4 | 5 | export default class GmmToolOptions extends Component { 6 | optionsData = optionsData; 7 | 8 | @action 9 | updateOpacity(id, event) { 10 | this.args.updateOptionValue(this.args.tool, id, event.target.value); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /addon/components/google-maps-markup.hbs: -------------------------------------------------------------------------------- 1 |
8 |

9 | Mode 10 |

11 | 12 |
13 | {{#each this.modes as |item|}} 14 | 26 | {{/each}} 27 |
28 | 29 |

30 | Tools 31 |

32 | 33 | {{#if (is-equal this.markupData.mode 'draw')}} 34 |
35 | {{#each this.drawTools as |tool|}} 36 | 44 | {{/each}} 45 |
46 | {{else if (is-equal this.markupData.mode 'measure')}} 47 |
48 | {{#each this.measureTools as |tool|}} 49 | 57 | {{/each}} 58 |
59 | {{/if}} 60 | 61 | {{! template-lint-disable simple-unless}} 62 | {{#unless (is-equal this.toolId 'pan')}} 63 | {{#if this.activeTool.options}} 64 |

65 | Options 66 |

67 | 71 | {{/if}} 72 | {{/unless}} 73 | 74 | 81 | 91 | 92 | 93 | {{yield}} 94 |
95 | -------------------------------------------------------------------------------- /addon/components/google-maps-markup.js: -------------------------------------------------------------------------------- 1 | import { inject as service } from '@ember/service'; 2 | import { tracked, cached } from '@glimmer/tracking'; 3 | import { alias } from '@ember/object/computed'; 4 | import { copy } from 'ember-copy'; 5 | import { run, next } from '@ember/runloop'; 6 | import { A as boundArray } from '@ember/array'; 7 | import { set, action } from '@ember/object'; 8 | import { v1 } from 'uuid'; 9 | import { Root } from 'ember-composability-tools'; 10 | import MODE from '../utils/modes'; 11 | import overlayToFeature from '../utils/overlay-to-feature'; 12 | import featureCenter from '../utils/feature-center'; 13 | import initMeasureLabel from '../utils/init-measure-label'; 14 | import mapLabelFactory from '../utils/map-label'; 15 | import dynamicLabelFactory from '../utils/dynamic-label'; 16 | import labelPlotter from '../utils/label-plotter'; 17 | 18 | const clearAllConfirm = 19 | 'Markup is unsaved. Do you wish to continue clearing all markup?'; 20 | 21 | export default class GoogleMapsMarkup extends Root { 22 | @service('markupData') markupData; 23 | 24 | // Start Attrs 25 | @cached 26 | get editable() { 27 | return this.args.editable ?? true; 28 | } 29 | 30 | @cached 31 | get panForOffscreen() { 32 | return this.args.panForOffscreen ?? true; 33 | } 34 | 35 | @cached 36 | get autoResetToPan() { 37 | return this.args.autoResetToPan ?? false; 38 | } 39 | 40 | @cached 41 | get map() { 42 | let map = this.args.map || this.markupData.map; 43 | 44 | return map; 45 | } 46 | 47 | get childComponents() { 48 | return [...this.children]; 49 | } 50 | // End Attrs 51 | 52 | @alias('markupData.layers') 53 | dataLayers; 54 | @alias('markupData.results') 55 | results; 56 | @alias('markupData.modes') 57 | modes; 58 | @alias('markupData.drawTools') 59 | drawTools; 60 | @alias('markupData.measureTools') 61 | measureTools; 62 | @alias('markupData.textGeoJson') 63 | textGeoJson; 64 | @alias('markupData.tools') 65 | tools; 66 | 67 | @tracked drawFinished; 68 | @tracked toolActive; 69 | @tracked lastActiveLayer; 70 | @tracked activeTool = undefined; 71 | @tracked toolId = undefined; 72 | 73 | listeners = boundArray(); 74 | toolListeners = boundArray(); 75 | currentPoints = boundArray(); 76 | resultsHidden = false; 77 | 78 | constructor() { 79 | super(...arguments); 80 | 81 | if (!window.google) { 82 | throw new Error('Sorry, but `window.google` is required for this addon'); 83 | } 84 | this.toolId = this.tools.pan.id; 85 | this.dm = new google.maps.drawing.DrawingManager({ 86 | drawingControl: false, 87 | }); 88 | this.MapLabel = mapLabelFactory(); 89 | this.currentLabel = new this.MapLabel(undefined, { 90 | dontScale: true, 91 | }); 92 | this.DynamicLabel = dynamicLabelFactory(); 93 | 94 | this.initPopupEvents(); 95 | } 96 | 97 | @action 98 | setup(_el, [map]) { 99 | if (!this.mapSetup && map) { 100 | this.mapSetup = true; 101 | this.setupLayers(map); 102 | this.changeMode(MODE.draw); 103 | } 104 | } 105 | 106 | initPopupEvents() { 107 | let editable = this.editable; 108 | 109 | if (editable) { 110 | let popup = new google.maps.InfoWindow(); 111 | 112 | popup.setContent(`
`); 113 | 114 | popup.addListener('closeclick', () => { 115 | set(popup, 'lastData.editing', false); 116 | set(popup, 'lastData', undefined); 117 | // cleanup? 118 | }); 119 | 120 | this.markupEditPopup = popup; 121 | } 122 | } 123 | 124 | addTextLabel(tool, position) { 125 | let autoResetToPan = this.autoResetToPan; 126 | let results = this.results; 127 | let mode = this.markupData.mode; 128 | let map = this.map; 129 | let style = copy(tool.style || {}); 130 | let labelMarker = new this.DynamicLabel(position, { 131 | color: style.color, 132 | autoFocus: true, 133 | fontSize: style.fontSize, 134 | onOver() { 135 | console.log('over'); 136 | labelMarker.highlight(); 137 | }, 138 | onOut() { 139 | console.log('out'); 140 | labelMarker.clearHighlight(); 141 | }, 142 | }); 143 | let item = { 144 | mode, 145 | style, 146 | isVisible: true, 147 | type: tool.id, 148 | feature: labelMarker, 149 | name: tool.name, 150 | options: tool.options, 151 | isEditable: Object.keys(style).length ? true : false, 152 | }; 153 | 154 | labelMarker.setMap(map); 155 | results.insertAt(0, item); 156 | map.setOptions({ draggableCursor: undefined }); 157 | // TODO: convert to geojson and add to active layer 158 | // later load during results process 159 | let feature = this.createFeature(item, labelMarker.position); 160 | let textGeoJson = this.textGeoJson; 161 | 162 | feature.toGeoJson((data) => { 163 | item.geojson = data; 164 | textGeoJson.pushObject(data); 165 | }); 166 | 167 | if (this.afterAddFeature) { 168 | this.afterAddFeature(item); 169 | } 170 | 171 | if (autoResetToPan) { 172 | this.toolNotFinished = true; 173 | google.maps.event.addListenerOnce(labelMarker, 'focusout', () => { 174 | run.later( 175 | this, 176 | function () { 177 | let freshTool = this.markupData.getTool(tool.id); 178 | let freshStyle = copy(freshTool.style); 179 | 180 | labelMarker.color = freshStyle.color; 181 | set(item, 'style', freshStyle); 182 | set(item, 'geojson.properties.style', freshStyle); 183 | this.toolNotFinished = false; 184 | 185 | this.changeTool(this.tools.pan.id); 186 | }, 187 | 250 188 | ); 189 | }); 190 | } 191 | 192 | this.drawFinished = true; 193 | } 194 | 195 | createFeature(result, geometry) { 196 | let id = v1(); 197 | let properties = { 198 | name: result.name, 199 | mode: result.mode, 200 | type: result.type, 201 | style: result.style, 202 | isVisible: true, 203 | }; 204 | let feature = new google.maps.Data.Feature({ 205 | geometry, 206 | properties, 207 | id, 208 | }); 209 | 210 | return feature; 211 | } 212 | 213 | enableFreeFormPolygon() { 214 | let autoResetToPan = this.autoResetToPan; 215 | let toolId = this.toolId; 216 | let map = this.map; 217 | let mode = this.markupData.mode; 218 | let activeLayer = this.activeLayer; 219 | let tool = this.markupData.getTool(toolId); 220 | let style = copy(tool.style || {}); 221 | let poly = new google.maps.Polyline({ 222 | map, 223 | clickable: false, 224 | }); 225 | 226 | this.toolActive = true; 227 | poly.setOptions(style); 228 | 229 | let move = google.maps.event.addListener(map, 'mousemove', (e) => { 230 | poly.getPath().push(e.latLng); 231 | map.setOptions({ draggable: false }); 232 | }); 233 | 234 | google.maps.event.addListenerOnce(map, 'click', () => { 235 | google.maps.event.removeListener(move); 236 | map.setOptions({ draggable: true }); 237 | poly.setMap(null); 238 | 239 | let path = poly.getPath(); 240 | let polygon = new google.maps.Data.Polygon([path.getArray()]); 241 | let item = { 242 | mode, 243 | style, 244 | isVisible: true, 245 | type: tool.id, 246 | name: tool.name, 247 | }; 248 | let feature = this.createFeature(item, polygon); 249 | 250 | if (this.afterAddFeature) { 251 | this.afterAddFeature(item); 252 | } 253 | 254 | poly = null; 255 | activeLayer.data.add(feature); 256 | activeLayer.data.overrideStyle(feature, style); 257 | 258 | if (autoResetToPan) { 259 | run.later( 260 | this, 261 | function () { 262 | this.changeTool(this.tools.pan.id); 263 | }, 264 | 250 265 | ); 266 | } 267 | 268 | this.drawFinished = true; 269 | 270 | run.later( 271 | this, 272 | () => { 273 | this.toolActive = false; 274 | }, 275 | 250 276 | ); 277 | }); 278 | } 279 | 280 | @action 281 | updateOptionValue(tool, prop, value) { 282 | set(tool, prop, value); 283 | } 284 | 285 | @action 286 | changeMode(mode) { 287 | this.markupData.mode = mode.id; 288 | this.changeLayer(); 289 | this.changeTool(this.toolId); 290 | } 291 | 292 | @action 293 | fillColorTransparent() { 294 | set( 295 | this.activeTool, 296 | 'fillColorTransparent', 297 | !this.activeTool.fillColorTransparent 298 | ); 299 | 300 | if (this.activeTool.fillColorTransparent) { 301 | set(this.activeTool, 'style.fillOpacity', 0.5); 302 | } else { 303 | set(this.activeTool, 'style.fillOpacity', 0); 304 | } 305 | } 306 | 307 | @action 308 | changeTool(toolId) { 309 | let markupDataService = this.markupData; 310 | let activeLayer = this.activeLayer; 311 | let map = this.map; 312 | let dm = this.dm; 313 | let tool = this.markupData.getTool(toolId); 314 | let listeners = this.toolListeners; 315 | 316 | this.activeTool = tool; 317 | this.drawFinished = false; 318 | markupDataService.set('activeTool', tool.id); 319 | 320 | this.resetAllLayers(); 321 | this.clearListeners(); 322 | dm.setDrawingMode(null); 323 | map.setOptions({ draggableCursor: 'default' }); 324 | 325 | if (activeLayer) { 326 | activeLayer.data.setDrawingMode(null); 327 | 328 | if (tool.id === 'pan') { 329 | let clickListener = activeLayer.data.addListener('click', (event) => { 330 | let found = this.childComponents.find(function (comp) { 331 | return comp.args.data?.feature.getId() === event.feature.getId(); 332 | }); 333 | 334 | if (found) { 335 | // invoke action on the component 336 | found.send('edit', event.latLng); 337 | } 338 | }); 339 | let mouseoverListener = activeLayer.data.addListener( 340 | 'mouseover', 341 | (event) => { 342 | let found = this.childComponents.find(function (comp) { 343 | return comp.args.data?.feature.getId() === event.feature.getId(); 344 | }); 345 | 346 | if (found) { 347 | // invoke action here 348 | this.highlightResult(found.args.data); 349 | } 350 | } 351 | ); 352 | let mouseoutListener = activeLayer.data.addListener('mouseout', () => { 353 | this.childComponents.forEach((comp) => { 354 | this.resetResultStyle(comp.args.data); 355 | }); 356 | }); 357 | 358 | listeners.pushObjects([ 359 | clickListener, 360 | mouseoverListener, 361 | mouseoutListener, 362 | ]); 363 | } else if (tool.id === 'text') { 364 | map.setOptions({ draggableCursor: 'crosshair' }); 365 | 366 | let mapListener = map.addListener('click', (event) => { 367 | if (this.toolNotFinished) { 368 | return; 369 | } 370 | this.addTextLabel(tool, event.latLng); 371 | map.setOptions({ draggableCursor: 'default' }); 372 | event.stop(); 373 | }); 374 | listeners.pushObject(mapListener); 375 | 376 | this.dataLayers.forEach((layer) => { 377 | let dataListener = layer.data.addListener('click', (event) => { 378 | this.addTextLabel(tool, event.latLng); 379 | map.setOptions({ draggableCursor: 'default' }); 380 | event.stop(); 381 | }); 382 | listeners.pushObject(dataListener); 383 | }); 384 | } else if (tool.dataId) { 385 | let style = copy(tool.style || {}); 386 | 387 | map.setOptions({ draggableCursor: 'crosshair' }); 388 | activeLayer.data.setDrawingMode(tool.dataId); 389 | activeLayer.data.setStyle(style); 390 | } else if (tool.dmId) { 391 | let style = copy(tool.style || {}); 392 | 393 | map.setOptions({ draggableCursor: 'crosshair' }); 394 | dm.setDrawingMode(tool.dmId); 395 | dm.setOptions({ 396 | [`${tool.id}Options`]: style, 397 | }); 398 | dm.setMap(map); 399 | } else { 400 | // for freeform polygon and others 401 | map.setOptions({ draggableCursor: 'crosshair' }); 402 | } 403 | } 404 | 405 | this.toolId = toolId; 406 | } 407 | 408 | @action 409 | toggleResults() { 410 | let isHidden = this.toggleProperty('resultsHidden'); 411 | let activeLayer = this.activeLayer; 412 | let results = this.results; 413 | 414 | results.forEach((result) => this.toggleResult(result, !isHidden)); 415 | activeLayer.isHidden = isHidden; 416 | } 417 | 418 | @action 419 | clearResults() { 420 | if (confirm(clearAllConfirm)) { 421 | let mode = this.markupData.mode; 422 | let layer = this.activeLayer; 423 | let results = this.results; 424 | let textGeoJson = this.textGeoJson; 425 | 426 | layer.data.forEach((feature) => { 427 | layer.data.remove(feature); 428 | }); 429 | 430 | results.forEach((result) => { 431 | if (mode === 'measure') { 432 | result.label.onRemove(); 433 | } else if (result.feature.setMap) { 434 | // remove text marker 435 | result.feature.setMap(null); 436 | if (result.type === 'text') { 437 | textGeoJson.removeObject(result.geojson); 438 | } 439 | } 440 | }); 441 | 442 | results.clear(); 443 | 444 | if (this.afterClearResults) { 445 | this.afterClearResults(layer); 446 | } 447 | } 448 | } 449 | 450 | @action 451 | removeResult(result) { 452 | let mode = this.markupData.mode; 453 | let results = this.results; 454 | let layer = this.activeLayer; 455 | let textGeoJson = this.textGeoJson; 456 | 457 | if (result.type === 'text') { 458 | result.feature.setMap(null); 459 | textGeoJson.removeObject(result.geojson); 460 | } else { 461 | layer.data.remove(result.feature); 462 | } 463 | 464 | if (mode === 'measure') { 465 | result.label.onRemove(); 466 | } 467 | 468 | results.removeObject(result); 469 | } 470 | 471 | /** 472 | * Toggle show/hide of a result. 473 | * 474 | * @param {Object} result The result object to toggle. 475 | * @param {Boolean} force Override the toggle, true for show and false for hide. 476 | */ 477 | @action 478 | toggleResult(result, force) { 479 | let layer = this.activeLayer; 480 | let mode = this.markupData.mode; 481 | let isMeasure = mode === 'measure'; 482 | let hide = 483 | force !== undefined && force !== null 484 | ? !force 485 | : result.type === 'text' 486 | ? result.feature.visible 487 | : layer.data.contains(result.feature); 488 | 489 | if (hide) { 490 | set(result, 'isVisible', false); 491 | 492 | if (result.type === 'text') { 493 | result.feature.hide(); 494 | } else { 495 | result.feature.setProperty('isVisible', false); 496 | layer.data.remove(result.feature); 497 | 498 | if (isMeasure) { 499 | result.label.hide(); 500 | } 501 | } 502 | } else { 503 | set(result, 'isVisible', true); 504 | 505 | if (result.type === 'text') { 506 | result.feature.show(); 507 | } else { 508 | result.feature.setProperty('isVisible', true); 509 | layer.data.add(result.feature); 510 | 511 | if (isMeasure) { 512 | result.label.show(); 513 | } 514 | } 515 | } 516 | } 517 | 518 | @action 519 | editResult(data, guid) { 520 | let popup = this.markupEditPopup; 521 | let map = this.map; 522 | let editable = this.editable; 523 | let childComponents = this.childComponents; 524 | 525 | set(data, 'editing', true); 526 | 527 | // disable editing on other items 528 | childComponents.forEach((comp) => { 529 | if (comp.guid !== guid) { 530 | set(comp, 'data.editing', false); 531 | } 532 | }); 533 | 534 | if (!editable) { 535 | return; 536 | } 537 | 538 | if (popup.getPosition()) { 539 | popup.close(); 540 | 541 | if (popup.lastData) { 542 | set(popup, 'lastData.editing', false); 543 | } 544 | } 545 | 546 | if (data) { 547 | let latlng = featureCenter(data.feature); 548 | 549 | if (!latlng) { 550 | return; 551 | } 552 | 553 | popup.setOptions({ 554 | pixelOffset: new google.maps.Size(0, 0), 555 | }); 556 | popup.setPosition(latlng); 557 | popup.open(map); 558 | popup.lastData = data; 559 | } 560 | } 561 | 562 | @action 563 | highlightResult(data) { 564 | let layer = this.activeLayer; 565 | let style; 566 | 567 | this.panToIfHidden(data.feature); 568 | 569 | if (data.type === 'marker') { 570 | style = { 571 | icon: { 572 | url: 'google-maps-markup/images/spotlight-poi-highlighted_hdpi.png', 573 | scaledSize: new google.maps.Size(22, 40), 574 | }, 575 | }; 576 | } else if (data.type === 'text') { 577 | data.feature.highlight(); 578 | } else { 579 | style = { 580 | strokeColor: 'red', 581 | zIndex: 99999, 582 | }; 583 | } 584 | 585 | if (data.label) { 586 | data.label.highlight(); 587 | } 588 | 589 | layer.data.overrideStyle(data.feature, style); 590 | } 591 | 592 | @action 593 | resetResultStyle(data) { 594 | let layer = this.activeLayer; 595 | 596 | if (!data.editingShape) { 597 | if (data.type === 'text') { 598 | data.feature.clearHighlight(); 599 | } else { 600 | layer.data.revertStyle(data.feature); 601 | if (data.style) { 602 | layer.data.overrideStyle(data.feature, data.style); 603 | } 604 | 605 | if (data.label) { 606 | data.label.clearHighlight(); 607 | } 608 | } 609 | } 610 | 611 | if (!data.editing) { 612 | this.panBack(); 613 | } 614 | } 615 | 616 | resetAllLayers() { 617 | let layers = this.dataLayers; 618 | 619 | layers.forEach((layer) => { 620 | layer.data.setDrawingMode(null); 621 | }); 622 | } 623 | 624 | clearListeners() { 625 | let listeners = this.toolListeners; 626 | 627 | listeners.forEach((l) => google.maps.event.removeListener(l)); 628 | listeners.clear(); 629 | } 630 | 631 | panToIfHidden(feature) { 632 | let panForOffscreen = this.panForOffscreen; 633 | 634 | if (!panForOffscreen) { 635 | return; 636 | } 637 | 638 | let map = this.map; 639 | let center = featureCenter(feature); 640 | let bounds = map.getBounds(); 641 | 642 | if (!center) { 643 | return; 644 | } 645 | 646 | this.originalCenter = map.getCenter(); 647 | 648 | if (!bounds.contains(center)) { 649 | map.panTo(center); 650 | } 651 | } 652 | 653 | panBack() { 654 | let panForOffscreen = this.panForOffscreen; 655 | 656 | if (!panForOffscreen) { 657 | return; 658 | } 659 | 660 | let map = this.map; 661 | let center = this.originalCenter; 662 | 663 | if (center) { 664 | map.setCenter(center); 665 | } 666 | } 667 | 668 | changeLayer() { 669 | let modeId = this.markupData.mode; 670 | let map = this.map; 671 | let toolId = this.toolId; 672 | let dataLayers = this.dataLayers; 673 | let activeLayer = this.activeLayer; 674 | 675 | this.lastActiveLayer = activeLayer; 676 | 677 | if (modeId === MODE.draw.id || modeId === MODE.measure.id) { 678 | let tool = this.markupData.getTool(toolId, modeId); 679 | 680 | activeLayer = dataLayers[modeId === MODE.draw.id ? 0 : 1]; 681 | 682 | if (!activeLayer.isHidden) { 683 | activeLayer.data.setMap(map); 684 | } 685 | 686 | // tool doesn't exist for this mode, revert to pan 687 | if (!tool) { 688 | this.changeTool(this.tools.pan.id); 689 | } 690 | 691 | activeLayer.data.setDrawingMode(tool && tool.dataId); 692 | 693 | this.activeLayer = activeLayer; 694 | this.setupActiveLayer(); 695 | } 696 | } 697 | 698 | setupActiveLayer() { 699 | let mode = this.markupData.mode; 700 | let layer = this.activeLayer; 701 | let lastLayer = this.lastActiveLayer; 702 | 703 | if (!layer) { 704 | return; 705 | } 706 | 707 | if (lastLayer) { 708 | google.maps.event.clearListeners(lastLayer.data, 'addfeature'); 709 | } 710 | 711 | let listener = layer.data.addListener( 712 | 'addfeature', 713 | run.bind(this, (event) => { 714 | if (event.feature.getProperty('skip')) { 715 | return; 716 | } 717 | 718 | let map = this.map; 719 | let tool = this.activeTool; 720 | let toolId = this.toolId; 721 | let results = this.results; 722 | let found = results.find(function (item) { 723 | if (item.feature && item.feature.getId) { 724 | return item.feature.getId() === event.feature.getId(); 725 | } else if (item.feature) { 726 | return item.feature === event.feature; 727 | } 728 | }); 729 | 730 | if (!found) { 731 | let fillColorTransparent = copy(tool.fillColorTransparent); 732 | let style = copy(tool.style || {}); 733 | 734 | event.feature.setProperty('name', tool.name); 735 | event.feature.setProperty('mode', mode); 736 | event.feature.setProperty('type', toolId); 737 | event.feature.setProperty('isVisible', true); 738 | event.feature.setProperty('style', style); 739 | event.feature.setProperty( 740 | 'fillColorTransparent', 741 | fillColorTransparent 742 | ); 743 | event.feature.setProperty('distanceUnitId', tool.distanceUnitId); 744 | 745 | let item = { 746 | mode, 747 | layer, 748 | style, 749 | fillColorTransparent, 750 | isVisible: true, 751 | type: toolId, 752 | name: tool.name, 753 | feature: event.feature, 754 | options: tool.options, 755 | distanceUnitId: tool.distanceUnitId, 756 | isEditable: Object.keys(style).length ? true : false, 757 | }; 758 | 759 | if (item.style) { 760 | item.style.zIndex = 111; 761 | layer.data.overrideStyle(event.feature, item.style); 762 | } 763 | 764 | initMeasureLabel(item, map); 765 | results.insertAt(0, item); 766 | 767 | if (this.afterAddFeature) { 768 | this.afterAddFeature(item); 769 | } 770 | 771 | let autoResetToPan = this.autoResetToPan; 772 | 773 | if (autoResetToPan) { 774 | run.later( 775 | this, 776 | function () { 777 | this.changeTool(this.tools.pan.id); 778 | }, 779 | 250 780 | ); 781 | } 782 | 783 | this.drawFinished = true; 784 | } 785 | }) 786 | ); 787 | 788 | this.listeners.pushObjects([listener]); 789 | } 790 | 791 | @action 792 | setupLayers(map) { 793 | let dm = this.dm; 794 | let results = this.results; 795 | let layers = this.dataLayers; 796 | 797 | // Enable all layers to show on map 798 | layers.forEach((layer) => layer.data.setMap(map)); 799 | 800 | let listener = dm.addListener( 801 | 'overlaycomplete', 802 | run.bind(this, (event) => { 803 | let activeLayer = this.activeLayer; 804 | let feature = overlayToFeature(event.type, event.overlay, results); 805 | 806 | event.overlay.setMap(null); 807 | 808 | activeLayer.data.add(feature); 809 | }) 810 | ); 811 | 812 | this.listeners.pushObject(listener); 813 | 814 | if (!this.mapEventsSetup) { 815 | this.setupMapEvents(map); 816 | } 817 | } 818 | 819 | setupMapEvents(map) { 820 | let currentPoints = this.currentPoints; 821 | let currentLabel = this.currentLabel; 822 | 823 | if (map) { 824 | this.mapEventsSetup = true; 825 | 826 | let body = document.body; 827 | let plotter; 828 | 829 | let onClick = run.bind(this, (event) => { 830 | let mode = this.markupData.mode; 831 | let toolId = this.toolId; 832 | let toolActive = this.toolActive; 833 | let tool = this.markupData.getTool(toolId, mode); 834 | let mapDiv = map.getDiv(); 835 | let target = event.target; 836 | let withinMap = mapDiv.contains(target); 837 | let results = this.results; 838 | let data = results.lastObject; 839 | 840 | // Set distanceUnitId if not set yet and available 841 | if (data && withinMap && !data.distanceUnitId && tool.distanceUnitId) { 842 | data.distanceUnitId = tool.distanceUnitId; 843 | } 844 | 845 | if (mode === 'draw') { 846 | if ( 847 | withinMap && 848 | toolActive !== true && 849 | toolId === 'freeFormPolygon' 850 | ) { 851 | next(() => { 852 | this.enableFreeFormPolygon(); 853 | }); 854 | return; 855 | } 856 | 857 | if (toolActive === false) { 858 | this.toolActive = undefined; 859 | return; 860 | } 861 | 862 | return; 863 | } 864 | 865 | let toolIsPan = toolId === 'pan'; 866 | let drawFinished = this.drawFinished; 867 | let noPoints = !currentPoints.length; 868 | 869 | if (toolIsPan || (noPoints && drawFinished)) { 870 | return; 871 | } 872 | 873 | if (withinMap && noPoints && !drawFinished) { 874 | let latlng = calculateLatLng(map, event); 875 | currentPoints.push(latlng); 876 | plotter = labelPlotter( 877 | currentLabel, 878 | currentPoints, 879 | toolId, 880 | event, 881 | map, 882 | tool.distanceUnitId 883 | ); 884 | } else if (withinMap && !toolIsPan && !drawFinished) { 885 | let latlng = calculateLatLng(map, event); 886 | currentPoints.push(latlng); 887 | } else if (plotter && drawFinished) { 888 | plotter.finish(); 889 | plotter = undefined; 890 | } 891 | }); 892 | 893 | let onDblClick = run.bind(this, (event) => { 894 | if (plotter) { 895 | plotter.finish(); 896 | plotter = undefined; 897 | } 898 | event.stopPropagation(); 899 | event.preventDefault(); 900 | }); 901 | 902 | let onMouseMove = run.bind(this, (event) => { 903 | if (plotter) { 904 | let latlng = calculateLatLng(map, event); 905 | plotter.update(currentPoints.concat(latlng)); 906 | } 907 | }); 908 | 909 | // Setup raw click handling - workaround for no basic events for drawing 910 | body.addEventListener('click', onClick); 911 | body.addEventListener('dblclick', onDblClick); 912 | body.addEventListener('mousemove', onMouseMove); 913 | 914 | this.bodyListeners = [ 915 | { event: 'click', handler: onClick }, 916 | { event: 'dblclick', handler: onDblClick }, 917 | { event: 'mousemove', handler: onMouseMove }, 918 | ]; 919 | } 920 | } 921 | 922 | @action 923 | beforeRemove() { 924 | let listeners = this.listeners; 925 | let bodyListeners = this.bodyListeners; 926 | 927 | this.changeTool(this.tools.pan.id); 928 | 929 | // Cleanup all listeners 930 | if (listeners) { 931 | listeners.forEach((listener) => { 932 | google.maps.event.removeListener(listener); 933 | }); 934 | } 935 | 936 | if (bodyListeners) { 937 | let body = document.body; 938 | 939 | bodyListeners.forEach((listener) => { 940 | body.removeEventListener(listener.event, listener.handler); 941 | }); 942 | } 943 | } 944 | } 945 | 946 | function calculatePosition(mapPosition, event) { 947 | let mapLeft = mapPosition.left; 948 | let mapTop = mapPosition.top; 949 | let x = event.pageX; 950 | let y = event.pageY; 951 | 952 | return { 953 | x: x - mapLeft, 954 | y: y - mapTop, 955 | }; 956 | } 957 | 958 | function calculateLatLng(map, event) { 959 | let mapEl = map.getDiv(); 960 | let projection = map.getProjection(); 961 | let bounds = map.getBounds(); 962 | let ne = bounds.getNorthEast(); 963 | let sw = bounds.getSouthWest(); 964 | let topRight = projection.fromLatLngToPoint(ne); 965 | let bottomLeft = projection.fromLatLngToPoint(sw); 966 | let mapPosition = getOffset(mapEl); 967 | let pos = calculatePosition(mapPosition, event); 968 | let scale = 1 << map.getZoom(); 969 | let point = new google.maps.Point( 970 | pos.x / scale + bottomLeft.x, 971 | pos.y / scale + topRight.y 972 | ); 973 | let latlng = projection.fromPointToLatLng(point); 974 | 975 | return latlng; 976 | } 977 | 978 | function getOffset(element) { 979 | if (!element.getClientRects().length) { 980 | return { top: 0, left: 0 }; 981 | } 982 | 983 | let rect = element.getBoundingClientRect(); 984 | let win = element.ownerDocument.defaultView; 985 | return { 986 | top: rect.top + win.pageYOffset, 987 | left: rect.left + win.pageXOffset, 988 | }; 989 | } 990 | -------------------------------------------------------------------------------- /addon/helpers/is-equal.js: -------------------------------------------------------------------------------- 1 | import { helper } from '@ember/component/helper'; 2 | 3 | export function isEqual(params /*, hash*/) { 4 | return params[0] === params[1]; 5 | } 6 | 7 | export default helper(isEqual); 8 | -------------------------------------------------------------------------------- /addon/helpers/tool-id.js: -------------------------------------------------------------------------------- 1 | import { helper } from '@ember/component/helper'; 2 | import { get } from '@ember/object'; 3 | 4 | export function toolId([tool] /*, hash*/) { 5 | return get(tool, 'id') || get(tool, 'type'); 6 | } 7 | 8 | export default helper(toolId); 9 | -------------------------------------------------------------------------------- /addon/services/markup-data.js: -------------------------------------------------------------------------------- 1 | import Service from '@ember/service'; 2 | import { tracked } from '@glimmer/tracking'; 3 | import EmberObject, { action, computed, get } from '@ember/object'; 4 | import { A as boundArray } from '@ember/array'; 5 | import createFeature from '../utils/create-feature'; 6 | import initMeasureLabel from '../utils/init-measure-label'; 7 | import initTextLabel from '../utils/init-text-label'; 8 | import MODE from '../utils/modes'; 9 | import getTools from '../utils/tools'; 10 | import Layer from '../utils/layer'; 11 | 12 | const MODES = [MODE.draw.id, MODE.measure.id]; 13 | 14 | export default class MarkupData extends Service { 15 | @tracked mode = MODE.draw.id; 16 | markupResults = EmberObject.create({ 17 | draw: boundArray(), 18 | measure: boundArray(), 19 | }); 20 | textGeoJson = boundArray(); 21 | modes = [MODE.draw, MODE.measure]; 22 | tools = getTools(); 23 | 24 | constructor() { 25 | super(...arguments); 26 | let tools = this.tools; 27 | this.drawTools = boundArray([ 28 | tools.pan, 29 | tools.text, 30 | tools.marker, 31 | tools.polyline, 32 | tools.circle, 33 | tools.rectangle, 34 | tools.polygon, 35 | tools.freeFormPolygon, 36 | ]); 37 | this.measureTools = boundArray([ 38 | tools.pan, 39 | tools.polyline, 40 | tools.circle, 41 | tools.rectangle, 42 | tools.polygon, 43 | ]); 44 | } 45 | 46 | activate(map) { 47 | this.set('map', map); 48 | 49 | let layers = this.layers; 50 | let measureResults = this.get('markupResults.measure'); 51 | 52 | // Enable all layers to show on map 53 | layers.forEach((layer) => { 54 | layer.data.setMap(map); 55 | }); 56 | 57 | // Init measure labels 58 | measureResults.forEach((result) => { 59 | if (result.label) { 60 | result.label.setMap(map); 61 | } else { 62 | initMeasureLabel(result, map); 63 | } 64 | }); 65 | } 66 | 67 | changeModeByResults() { 68 | let markupResults = this.markupResults; 69 | 70 | for (let i = 0; i < MODES.length; i++) { 71 | let key = MODES[i]; 72 | let modeResults = get(markupResults, key); 73 | 74 | if (modeResults && modeResults.length) { 75 | this.set('mode', key); 76 | return; 77 | } 78 | } 79 | } 80 | 81 | get layers() { 82 | if (this._cachedLayers) { 83 | return this._cachedLayers; 84 | } 85 | let results = this.results; 86 | let textGeoJson = this.textGeoJson; 87 | let setId = function (geom) { 88 | return createFeature(geom, results); 89 | }; 90 | 91 | let items = [ 92 | new Layer({ 93 | textGeoJson, 94 | isHidden: false, 95 | data: new google.maps.Data({ featureFactory: setId }), 96 | }), 97 | new Layer({ 98 | isHidden: false, 99 | data: new google.maps.Data({ featureFactory: setId }), 100 | }), 101 | ]; 102 | 103 | this._cachedLayers = items; 104 | 105 | return items; 106 | } 107 | 108 | @computed('mode') 109 | get results() { 110 | let mode = this.mode; 111 | 112 | if (!mode) { 113 | return undefined; 114 | } 115 | 116 | return this.get(`markupResults.${mode}`); 117 | } 118 | 119 | set results(data) { 120 | let mode = this.mode; 121 | 122 | if (!mode) { 123 | return; 124 | } 125 | 126 | this.set(`markupResults.${mode}`, data); 127 | 128 | return data; 129 | } 130 | 131 | @action 132 | getTool(id, mode) { 133 | if (!id) { 134 | id = this.tools.pan.id; 135 | } 136 | let toolIds = mode ? this.get(mode + 'Tools') : this.tools; 137 | 138 | return Array.isArray(toolIds) ? toolIds.findBy('id', id) : toolIds[id]; 139 | } 140 | 141 | @action 142 | featureToResult(feature, layer) { 143 | let map = this.map; 144 | let textGeoJson = this.textGeoJson; 145 | let name = feature.getProperty('name'); 146 | let mode = feature.getProperty('mode'); 147 | let style = feature.getProperty('style'); 148 | let type = feature.getProperty('type'); 149 | let results = this.get(`markupResults.${mode}`); 150 | let tool = this.getTool(type, mode); 151 | let result = { 152 | mode, 153 | layer, 154 | style, 155 | fillColorTransparent: feature.getProperty('fillColorTransparent'), 156 | isVisible: feature.getProperty('isVisible'), 157 | type, 158 | name, 159 | feature, 160 | options: tool?.options, 161 | distanceUnitId: feature.getProperty('distanceUnitId'), 162 | isEditable: Object.keys(style).length ? true : false, 163 | }; 164 | 165 | if (result.style) { 166 | layer.data.overrideStyle(feature, result.style); 167 | } 168 | 169 | initMeasureLabel(result, map); 170 | initTextLabel(result, layer, map); 171 | 172 | // Put text into temp geojson table for export 173 | if (textGeoJson && result.type === 'text') { 174 | feature.toGeoJson((data) => { 175 | result.geojson = data; 176 | textGeoJson.pushObject(data); 177 | }); 178 | } 179 | 180 | results.pushObject(result); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /addon/utils/area.js: -------------------------------------------------------------------------------- 1 | import formatNumber from './format-number'; 2 | 3 | const SQUARE_FEET_AREA_CONVERT_MAP = { 4 | acres: 2.29568e-5, 5 | 'sq km': 9.2903e-8, 6 | 'sq mi': 3.587e-8, 7 | 'sq ft': 1, 8 | }; 9 | const NEXT_UNIT = { 10 | 'sq ft': { 11 | unit: 'acres', 12 | threshold: 43560, 13 | }, 14 | acres: { 15 | unit: 'sq mi', 16 | threshold: 27880000, 17 | }, 18 | }; 19 | 20 | export default function acres(result) { 21 | let unitId = result.unit.id; 22 | let output = {}; 23 | 24 | while (nextUnitIfBeyondThreshold(result.value, unitId)) { 25 | let nextOptions = NEXT_UNIT[unitId]; 26 | 27 | unitId = nextOptions.unit; 28 | } 29 | 30 | let converted = result.value * SQUARE_FEET_AREA_CONVERT_MAP[unitId]; 31 | 32 | output.value = formatNumber(converted); 33 | output.unit = unitId; 34 | output.measurementType = result.measurementType; 35 | 36 | return output.value !== undefined ? output : result; 37 | } 38 | 39 | function nextUnitIfBeyondThreshold(value, unit) { 40 | let nextOptions = NEXT_UNIT[unit]; 41 | 42 | if (!nextOptions) { 43 | return false; 44 | } 45 | 46 | if (value > nextOptions.threshold) { 47 | return true; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /addon/utils/create-circle.js: -------------------------------------------------------------------------------- 1 | const d2r = Math.PI / 180; // degrees to radians 2 | const r2d = 180 / Math.PI; // radians to degrees 3 | const earthsradius = 3963; // 3963 is the radius of the earth in miles 4 | 5 | export default function createCircle(lat, lng, radius) { 6 | var points = 32; 7 | 8 | // radius in miles 9 | radius = radius * 0.000621371; 10 | 11 | // find the raidus in lat/lon 12 | var rlat = (radius / earthsradius) * r2d; 13 | var rlng = rlat / Math.cos(lat * d2r); 14 | var path = []; 15 | 16 | // one extra here makes sure we connect the 17 | for (let i = 0; i < points + 1; i++) { 18 | let theta = Math.PI * (i / (points / 2)); 19 | let ex = lng + rlng * Math.cos(theta); // center a + radius x * cos(theta) 20 | let ey = lat + rlat * Math.sin(theta); // center b + radius y * sin(theta) 21 | path.push(new google.maps.LatLng(ey, ex)); 22 | } 23 | 24 | return path; 25 | } 26 | -------------------------------------------------------------------------------- /addon/utils/create-feature.js: -------------------------------------------------------------------------------- 1 | import guid from './guid'; 2 | 3 | export default function createFeature(geometry) { 4 | let feature = new google.maps.Data.Feature({ 5 | id: guid(), 6 | geometry, 7 | }); 8 | 9 | return feature; 10 | } 11 | -------------------------------------------------------------------------------- /addon/utils/distance.js: -------------------------------------------------------------------------------- 1 | import formatNumber from './format-number'; 2 | 3 | const FEET_DISTANCE_CONVERT_MAP = { 4 | mi: 0.000189394, 5 | km: 0.0003048, 6 | meter: 0.3048, 7 | ft: 1, 8 | }; 9 | const NEXT_UNIT = { 10 | ft: { 11 | unit: 'mi', 12 | threshold: 1320, 13 | }, 14 | meter: { 15 | unit: 'km', 16 | threshold: 1000, 17 | }, 18 | }; 19 | 20 | export default function miles(result) { 21 | let unitId = result.unit.id; 22 | let output = {}; 23 | 24 | while (nextUnitIfBeyondThreshold(result.value, unitId)) { 25 | let nextOptions = NEXT_UNIT[unitId]; 26 | 27 | unitId = nextOptions.unit; 28 | } 29 | 30 | let converted = result.value * FEET_DISTANCE_CONVERT_MAP[unitId]; 31 | 32 | output.value = formatNumber(converted); 33 | output.unit = unitId; 34 | output.measurementType = result.measurementType; 35 | 36 | return output.value !== undefined ? output : result; 37 | } 38 | 39 | function nextUnitIfBeyondThreshold(value, unit) { 40 | let nextOptions = NEXT_UNIT[unit]; 41 | 42 | if (!nextOptions) { 43 | return false; 44 | } 45 | 46 | if (value > nextOptions.threshold) { 47 | return true; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /addon/utils/dynamic-label.js: -------------------------------------------------------------------------------- 1 | import { v1 } from 'uuid'; 2 | import mapLabelFactory from './map-label'; 3 | let DynamicLabelLocal; 4 | 5 | function createDynamicLabel() { 6 | if (DynamicLabelLocal) { 7 | return DynamicLabelLocal; 8 | } 9 | const MapLabel = mapLabelFactory(); 10 | 11 | class DynamicLabel extends MapLabel { 12 | constructor(latlng, options) { 13 | options = options || {}; 14 | 15 | options.element = document.createElement('textarea'); 16 | options.element.placeholder = options.placeholder || 'Text Here'; 17 | options.element.rows = 1; 18 | options.element.wrap = 'hard'; 19 | 20 | super(latlng, options); 21 | 22 | this.autoFocus = options.autoFocus || false; 23 | this.editLabelInPlace = options.editLabelInPlace; 24 | this.center = false; 25 | 26 | if (this.editLabelInPlace === undefined) { 27 | this.editLabelInPlace = true; 28 | } 29 | 30 | this.fontSize = options.fontSize; 31 | this.label = options.label; 32 | this.onOver = options.onOver; 33 | this.onOut = options.onOut; 34 | this.id = v1(); 35 | } 36 | 37 | onAdd() { 38 | let panes = this.getPanes(); 39 | let pane = panes.overlayMouseTarget; 40 | let map = this.getMap(); 41 | 42 | if (pane) { 43 | pane.appendChild(this._element); 44 | 45 | if (this.autoFocus) { 46 | this._element.focus(); 47 | } 48 | } 49 | 50 | if (this.editLabelInPlace) { 51 | //this._element.contentEditable = true; 52 | google.maps.event.addDomListener(this._element, 'keydown', (event) => { 53 | // left, up, right, down, equal, minus 54 | let blockedKeys = [37, 38, 39, 40, 187, 189]; 55 | 56 | if (blockedKeys.indexOf(event.keyCode) !== -1) { 57 | event.stopPropagation(); 58 | } 59 | }); 60 | google.maps.event.addDomListener(this._element, 'dblclick', (event) => { 61 | event.stopPropagation(); 62 | }); 63 | google.maps.event.addDomListener( 64 | this._element, 65 | 'mousemove', 66 | (event) => { 67 | event.stopPropagation(); 68 | } 69 | ); 70 | google.maps.event.addDomListener(this._element, 'click', (event) => { 71 | this._element.focus(); 72 | event.stopPropagation(); 73 | }); 74 | google.maps.event.addDomListener( 75 | this._element, 76 | 'mouseover', 77 | (event) => { 78 | event.stopPropagation(); 79 | if (this.onOver) { 80 | this.onOver(); 81 | } 82 | } 83 | ); 84 | google.maps.event.addDomListener(this._element, 'mouseout', (event) => { 85 | event.stopPropagation(); 86 | if (this.onOut) { 87 | this.onOut(); 88 | } 89 | }); 90 | google.maps.event.addDomListener(this._element, 'focusin', (event) => { 91 | this.editingText = true; 92 | event.stopPropagation(); 93 | }); 94 | google.maps.event.addDomListener(this._element, 'select', (event) => { 95 | event.stopPropagation(); 96 | }); 97 | 98 | google.maps.event.addDomListener(this._element, 'blur', (event) => { 99 | let contentBlank = 100 | !!this.label.length && this.label.trim().length === 0; 101 | 102 | if (!contentBlank) { 103 | this.editingText = false; 104 | } 105 | 106 | google.maps.event.trigger(this, 'focusout'); 107 | event.stopPropagation(); 108 | }); 109 | 110 | google.maps.event.addDomListener(this._element, 'input', (event) => { 111 | google.maps.event.trigger(this, 'changelabel'); 112 | this.updateSize(); 113 | 114 | if (map) { 115 | google.maps.event.trigger(map, 'resize'); 116 | } 117 | event.stopPropagation(); 118 | }); 119 | } 120 | } 121 | 122 | draw() { 123 | super.draw(); 124 | this.updateSize(); 125 | } 126 | 127 | updateSize() { 128 | if (!document.body) { 129 | return; 130 | } 131 | 132 | let content = this.label; 133 | let fontSize = Number(this.fontSize.replace('px', '')); 134 | let contentLines = content.split(/\r|\n/); 135 | let contentHeight = contentLines.length * fontSize; 136 | let contentWidth = contentLines.reduce((total, line) => { 137 | let div = document.createElement('div'); 138 | 139 | div.style.position = 'absolute'; 140 | div.style.visibility = 'hidden'; 141 | div.style.fontSize = this.fontSize; 142 | div.style.height = 'auto'; 143 | div.style.width = 'auto'; 144 | div.style.whiteSpace = 'nowrap'; 145 | div.textContent = line; 146 | document.body.appendChild(div); 147 | 148 | let width = div.clientWidth; 149 | 150 | document.body.removeChild(div); 151 | 152 | if (width > total) { 153 | total = width; 154 | } 155 | 156 | return total; 157 | }, 100); 158 | 159 | content = content.replace(/\n/g, '
'); 160 | 161 | this._element.style.height = contentHeight + 10 + 'px'; 162 | this._element.style.width = contentWidth + 20 + 'px'; 163 | } 164 | 165 | // To match Google's Data.Feature#getId() 166 | getId() { 167 | return this.id; 168 | } 169 | 170 | set editingText(val) { 171 | if (val) { 172 | this._element.classList.add('editing-text'); 173 | } else { 174 | this._element.classList.remove('editing-text'); 175 | } 176 | 177 | this._editingText = val; 178 | } 179 | 180 | get editingText() { 181 | return this._editingText; 182 | } 183 | 184 | set label(value) { 185 | this._element.value = value || ''; 186 | } 187 | 188 | get label() { 189 | return this._element.value; 190 | } 191 | 192 | set fontSize(value = 12) { 193 | this._element.style.fontSize = value + 'px'; 194 | this.updateSize(); 195 | } 196 | 197 | get fontSize() { 198 | return this._element.style.fontSize; 199 | } 200 | 201 | onRemove() { 202 | google.maps.event.clearInstanceListeners(this._element); 203 | 204 | super.onRemove(...arguments); 205 | } 206 | } 207 | 208 | DynamicLabelLocal = DynamicLabel; 209 | return DynamicLabelLocal; 210 | } 211 | 212 | export default createDynamicLabel; 213 | -------------------------------------------------------------------------------- /addon/utils/feature-center.js: -------------------------------------------------------------------------------- 1 | import pathsToBounds from './paths-to-bounds'; 2 | 3 | export default function featureCenter(feature) { 4 | // DynamicLabel 5 | if (!feature.getGeometry && feature.latlng) { 6 | return feature.latlng; 7 | } 8 | 9 | let geometry = feature.getGeometry(); 10 | let type = geometry.getType(); 11 | 12 | switch (type) { 13 | case 'Point': { 14 | return geometry.get(); 15 | } 16 | 17 | case 'Polygon': { 18 | let paths = geometry.getArray()[0].getArray(); 19 | let bounds = pathsToBounds(paths); 20 | 21 | return bounds.getCenter(); 22 | } 23 | 24 | case 'LineString': { 25 | let paths = geometry.getArray(); 26 | let bounds = pathsToBounds(paths); 27 | 28 | return bounds.getCenter(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /addon/utils/format-number.js: -------------------------------------------------------------------------------- 1 | export default function formatNumber(value) { 2 | if (value < 1) { 3 | // 4 decimal places 4 | value = Math.round(value * 10000) / 10000; 5 | } else if (value >= 1 && value < 10) { 6 | // 1 decimal place 7 | value = Math.round(value * 10) / 10; 8 | } else { 9 | value = Math.round(value); 10 | } 11 | 12 | return value; 13 | } 14 | -------------------------------------------------------------------------------- /addon/utils/get-measurement.js: -------------------------------------------------------------------------------- 1 | import { polygonArea, circleArea, rectangleArea } from './shape-area'; 2 | import pathDistance from './path-distance'; 3 | import measureTypeResult from './measure-type-result'; 4 | 5 | export default function getMeasurement(type, feature, distanceUnitId) { 6 | switch (type) { 7 | case 'polyline': { 8 | let geometry = feature.getGeometry(); 9 | let paths = geometry.getArray(); 10 | let distance = pathDistance(paths); 11 | 12 | return measureTypeResult(type, distance, distanceUnitId); 13 | } 14 | 15 | case 'polygon': { 16 | let area = polygonArea(feature); 17 | return measureTypeResult(type, area, distanceUnitId); 18 | } 19 | 20 | case 'circle': { 21 | let area = circleArea(feature); 22 | return measureTypeResult(type, area, distanceUnitId); 23 | } 24 | 25 | case 'rectangle': { 26 | let area = rectangleArea(feature); 27 | return measureTypeResult(type, area, distanceUnitId); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /addon/utils/guid.js: -------------------------------------------------------------------------------- 1 | export default function guid() { 2 | return `${s4()}${s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`; 3 | } 4 | 5 | function s4() { 6 | return Math.floor((1 + Math.random()) * 0x10000) 7 | .toString(16) 8 | .substring(1); 9 | } 10 | -------------------------------------------------------------------------------- /addon/utils/init-measure-label.js: -------------------------------------------------------------------------------- 1 | import mapLabelFactory from './map-label'; 2 | import featureCenter from './feature-center'; 3 | import getMeasurement from './get-measurement'; 4 | 5 | export default function initMeasureLabel(result, map) { 6 | if (!result) { 7 | return; 8 | } 9 | 10 | if (result.mode === 'measure' && !result.label) { 11 | let center = featureCenter(result.feature); 12 | let measurement = getMeasurement( 13 | result.type, 14 | result.feature, 15 | result.distanceUnitId 16 | ); 17 | const MapLabel = mapLabelFactory(); 18 | 19 | result.label = new MapLabel(center); 20 | result.label.label = `${measurement.value} ${measurement.unit.display}`; 21 | 22 | if (map) { 23 | result.label.setMap(map); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /addon/utils/init-text-label.js: -------------------------------------------------------------------------------- 1 | import featureCenter from './feature-center'; 2 | import factoryDynamicLabel from './dynamic-label'; 3 | 4 | export default function initTextLabel(result, layer, map) { 5 | if (!result) { 6 | return; 7 | } 8 | 9 | if (result.mode === 'draw' && result.type === 'text') { 10 | let layerFeature = result.feature; 11 | let center = featureCenter(layerFeature); 12 | let style = layerFeature.getProperty('style'); 13 | let DynamicLabel = factoryDynamicLabel(); 14 | 15 | result.feature = new DynamicLabel(center, { 16 | label: layerFeature.getProperty('label'), 17 | color: style && style.color, 18 | }); 19 | 20 | if (map) { 21 | result.feature.setMap(map); 22 | } 23 | 24 | if (layer && layer.data) { 25 | layer.data.remove(layerFeature); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /addon/utils/label-plotter.js: -------------------------------------------------------------------------------- 1 | import pathsToBounds from './paths-to-bounds'; 2 | import pathDistance from './path-distance'; 3 | import measureTypeResult from './measure-type-result'; 4 | 5 | export default function labelPlotter( 6 | label, 7 | points, 8 | type, 9 | event, 10 | map, 11 | distanceUnitId 12 | ) { 13 | if (type === 'circle') { 14 | label.position = points[0]; 15 | } 16 | 17 | return { 18 | update(points) { 19 | switch (type) { 20 | case 'pan': 21 | return; 22 | 23 | case 'circle': { 24 | if (points.length === 2) { 25 | let radius = google.maps.geometry.spherical.computeDistanceBetween( 26 | points[0], 27 | points[1] 28 | ); 29 | let area = Math.PI * (radius * radius); 30 | let result = measureTypeResult(type, area, distanceUnitId); 31 | 32 | label.label = `${result.value} ${result.unit.display}`; 33 | label.setMap(map); 34 | } 35 | break; 36 | } 37 | 38 | case 'polyline': { 39 | if (points.length >= 2) { 40 | let bounds = pathsToBounds(points); 41 | let distance = pathDistance(points); 42 | let result = measureTypeResult(type, distance, distanceUnitId); 43 | 44 | label.label = `${result.value} ${result.unit.display}`; 45 | label.position = bounds.getCenter(); 46 | label.setMap(map); 47 | } 48 | break; 49 | } 50 | 51 | case 'rectangle': { 52 | if (points.length === 2) { 53 | let calcPoints = [ 54 | points[0], 55 | new google.maps.LatLng(points[0].lat(), points[1].lng()), 56 | points[1], 57 | new google.maps.LatLng(points[1].lat(), points[0].lng()), 58 | ]; 59 | let bounds = pathsToBounds(calcPoints); 60 | let area = google.maps.geometry.spherical.computeArea(calcPoints); 61 | let result = measureTypeResult(type, area, distanceUnitId); 62 | 63 | label.label = `${result.value} ${result.unit.display}`; 64 | label.position = bounds.getCenter(); 65 | label.setMap(map); 66 | } 67 | break; 68 | } 69 | 70 | case 'polygon': { 71 | if (points.length < 3) { 72 | break; 73 | } 74 | // move on to default 75 | } 76 | 77 | // eslint-disable-next-line no-fallthrough 78 | default: { 79 | if (points.length > 1) { 80 | let bounds = pathsToBounds(points); 81 | let area = google.maps.geometry.spherical.computeArea(points); 82 | let result = measureTypeResult(type, area, distanceUnitId); 83 | label.label = `${result.value} ${result.unit.display}`; 84 | label.position = bounds.getCenter(); 85 | label.setMap(map); 86 | } 87 | } 88 | } 89 | }, 90 | 91 | finish() { 92 | label.setMap(null); 93 | points.removeObjects(points); 94 | }, 95 | }; 96 | } 97 | -------------------------------------------------------------------------------- /addon/utils/layer.js: -------------------------------------------------------------------------------- 1 | import RSVP from 'rsvp'; 2 | 3 | class Layer { 4 | constructor(options) { 5 | this.isHidden = options.isHidden || false; 6 | this.data = options.data; 7 | this.textData = options.textGeoJson || []; 8 | } 9 | 10 | toGeoJson() { 11 | let textData = this.textData; 12 | 13 | return new RSVP.Promise((resolve) => { 14 | this.data.toGeoJson((data) => { 15 | data.features = data.features.concat(textData); 16 | resolve(data); 17 | }); 18 | }); 19 | } 20 | } 21 | 22 | export default Layer; 23 | -------------------------------------------------------------------------------- /addon/utils/map-label.js: -------------------------------------------------------------------------------- 1 | let MapLabelLocal; 2 | 3 | export default function mapLabelFactory() { 4 | if (MapLabelLocal) { 5 | return MapLabelLocal; 6 | } 7 | 8 | class MapLabel extends google.maps.OverlayView { 9 | constructor(latlng, options) { 10 | super(...arguments); 11 | 12 | options = options || {}; 13 | 14 | this.latlng = latlng; 15 | this.dontScale = options.dontScale; 16 | this.options = options; 17 | 18 | this._element = options.element || document.createElement('div'); 19 | this._element.className = 'google-maps-markup-map-label set-width'; 20 | this._element.style.position = 'absolute'; 21 | this._element.style.transformOrigin = 'left top'; 22 | 23 | if (options.className) { 24 | this._element.className += ' ' + options.className; 25 | } 26 | 27 | // Requires element to be present 28 | this.color = options.color; 29 | this.label = options.label; 30 | this.center = true; 31 | } 32 | 33 | // Required by GMaps 34 | onAdd() { 35 | let panes = this.getPanes(); 36 | let pane = panes.markerLayer; 37 | 38 | if (pane) { 39 | pane.appendChild(this._element); 40 | } 41 | } 42 | 43 | // Required by GMaps 44 | draw() { 45 | let map = this.getMap(); 46 | 47 | if (!map || !this.latlng) { 48 | return; 49 | } 50 | 51 | let projection = this.getProjection(); 52 | 53 | if (!projection) { 54 | return; 55 | } 56 | 57 | let position = projection.fromLatLngToDivPixel(this.latlng); 58 | let div = this._element; 59 | 60 | if (position && position.x && position.y) { 61 | let width = this._element.clientWidth; 62 | let height = this._element.clientHeight; 63 | let center = this.center; 64 | 65 | div.style.display = 'block'; 66 | 67 | if (map) { 68 | let zoom = map.getZoom(); 69 | 70 | this.updateScale(zoom, this.lastZoom); 71 | this.lastZoom = zoom; 72 | } 73 | 74 | let left = center ? position.x - width / 2 : position.x; 75 | let top = center ? position.y - height / 2 : position.y; 76 | 77 | div.style.left = left + 'px'; 78 | div.style.top = top + 'px'; 79 | } else { 80 | div.style.display = 'none'; 81 | } 82 | } 83 | 84 | // Required by GMaps 85 | onRemove() { 86 | this._element.parentNode.removeChild(this._element); 87 | } 88 | 89 | set label(value) { 90 | this._element.textContent = value || ''; 91 | } 92 | 93 | get label() { 94 | return this._element.textContent; 95 | } 96 | 97 | set color(value) { 98 | this._element.style.color = value; 99 | } 100 | 101 | get color() { 102 | return this._element.style.color; 103 | } 104 | 105 | set position(value) { 106 | this.latlng = value; 107 | this.draw(); 108 | } 109 | 110 | get position() { 111 | return this.latlng; 112 | } 113 | 114 | get visible() { 115 | return this._element.style.display === 'none' ? false : true; 116 | } 117 | 118 | hide() { 119 | this._element.style.display = 'none'; 120 | } 121 | 122 | show() { 123 | this._element.style.display = 'block'; 124 | } 125 | 126 | updateScale(newZoom, oldZoom) { 127 | if (this.dontScale) { 128 | return; 129 | } 130 | 131 | if (oldZoom === undefined) { 132 | this._element.style.transform = 'scale(1)'; 133 | this.scale = 1; 134 | this.scaleMaxZoom = newZoom; 135 | return; 136 | } 137 | 138 | if (newZoom < oldZoom) { 139 | this.scale -= 0.2; 140 | } else if (newZoom > oldZoom) { 141 | this.scale += 0.2; 142 | } 143 | 144 | if (newZoom >= this.scaleMaxZoom) { 145 | this.scale = 1; 146 | } 147 | 148 | if (this.scale >= 0) { 149 | this._element.style.transform = `scale(${this.scale})`; 150 | } 151 | } 152 | 153 | highlight() { 154 | this._element.classList.add('highlighted'); 155 | } 156 | 157 | clearHighlight() { 158 | this._element.classList.remove('highlighted'); 159 | } 160 | } 161 | 162 | MapLabelLocal = MapLabel; 163 | return MapLabelLocal; 164 | } 165 | -------------------------------------------------------------------------------- /addon/utils/measure-type-result.js: -------------------------------------------------------------------------------- 1 | import { A } from '@ember/array'; 2 | import commaifyNumber from './number-commas'; 3 | import area from './area'; 4 | import distance from './distance'; 5 | import optionsData from './options-data'; 6 | 7 | export default function measureTypeResult(type, value, unitId) { 8 | let units = A(optionsData[type].distanceUnits); 9 | let result = { 10 | measurementType: 'Distance', 11 | unit: units && units.findBy('id', unitId), 12 | value: 0, 13 | }; 14 | 15 | switch (type) { 16 | case 'polyline': { 17 | // meters->feet conversion 18 | result.value = value * 3.28084; 19 | result = distance(result); 20 | // Reset the correct unit format 21 | result.unit = units && units.findBy('id', result.unit); 22 | break; 23 | } 24 | 25 | case 'circle': 26 | case 'rectangle': 27 | case 'polygon': { 28 | // sq. meters-> sq. feet 29 | result.value = value * 10.7639; 30 | result.measurementType = 'Area'; 31 | result = area(result); 32 | // Reset the correct unit format 33 | result.unit = units.findBy('id', result.unit); 34 | break; 35 | } 36 | } 37 | 38 | result.value = commaifyNumber(result.value); 39 | return result; 40 | } 41 | -------------------------------------------------------------------------------- /addon/utils/modes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | draw: { 3 | id: 'draw', 4 | name: 'Draw', 5 | icon: 'fa-paint-brush', 6 | title: 'Drawing mode', 7 | }, 8 | measure: { 9 | id: 'measure', 10 | name: 'Measure', 11 | icon: 'fa-arrows-h', 12 | title: 'Measurement mode', 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /addon/utils/number-commas.js: -------------------------------------------------------------------------------- 1 | export default function numberCommas(value) { 2 | let parts = value.toString().split('.'); 3 | 4 | parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ','); 5 | 6 | return parts.join('.'); 7 | } 8 | -------------------------------------------------------------------------------- /addon/utils/options-data.js: -------------------------------------------------------------------------------- 1 | export default { 2 | pan: { 3 | id: 'pan', 4 | }, 5 | text: { 6 | id: 'text', 7 | fontSizes: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], 8 | }, 9 | marker: { 10 | id: 'marker', 11 | }, 12 | polyline: { 13 | id: 'polyline', 14 | strokeWeights: [2, 4, 6, 8, 10], 15 | distanceUnits: [ 16 | { 17 | id: 'ft', 18 | display: 'Feet', 19 | }, 20 | { 21 | id: 'meter', 22 | display: 'Meters', 23 | }, 24 | { 25 | id: 'mi', 26 | display: 'Miles', 27 | }, 28 | { 29 | id: 'km', 30 | display: 'Kilometers', 31 | }, 32 | ], 33 | }, 34 | polygon: { 35 | id: 'polygon', 36 | strokeWeights: [2, 4, 6, 8, 10], 37 | distanceUnits: [ 38 | { 39 | id: 'sq ft', 40 | display: 'Sq Ft', 41 | }, 42 | { 43 | id: 'acres', 44 | display: 'Acres', 45 | }, 46 | { 47 | id: 'sq mi', 48 | display: 'Sq Miles', 49 | }, 50 | { 51 | id: 'sq km', 52 | display: 'Sq Km', 53 | }, 54 | ], 55 | }, 56 | freeFormPolygon: { 57 | id: 'freeFormPolygon', 58 | strokeWeights: [2, 4, 6, 8, 10], 59 | distanceUnits: [ 60 | { 61 | id: 'sq ft', 62 | display: 'Sq Ft', 63 | }, 64 | { 65 | id: 'acres', 66 | display: 'Acres', 67 | }, 68 | { 69 | id: 'sq mi', 70 | display: 'Sq Miles', 71 | }, 72 | { 73 | id: 'sq km', 74 | display: 'Sq Km', 75 | }, 76 | ], 77 | }, 78 | circle: { 79 | id: 'circle', 80 | strokeWeights: [2, 4, 6, 8, 10], 81 | distanceUnits: [ 82 | { 83 | id: 'sq ft', 84 | display: 'Sq Ft', 85 | }, 86 | { 87 | id: 'acres', 88 | display: 'Acres', 89 | }, 90 | { 91 | id: 'sq mi', 92 | display: 'Sq Miles', 93 | }, 94 | { 95 | id: 'sq km', 96 | display: 'Sq Km', 97 | }, 98 | ], 99 | }, 100 | rectangle: { 101 | strokeWeights: [2, 4, 6, 8, 10], 102 | distanceUnits: [ 103 | { 104 | id: 'sq ft', 105 | display: 'Sq Ft', 106 | }, 107 | { 108 | id: 'acres', 109 | display: 'Acres', 110 | }, 111 | { 112 | id: 'sq mi', 113 | display: 'Sq Miles', 114 | }, 115 | { 116 | id: 'sq km', 117 | display: 'Sq Km', 118 | }, 119 | ], 120 | }, 121 | }; 122 | -------------------------------------------------------------------------------- /addon/utils/overlay-to-feature.js: -------------------------------------------------------------------------------- 1 | import createCircle from './create-circle'; 2 | import createFeature from './create-feature'; 3 | 4 | export default function overlayToFeature(type, overlay) { 5 | var paths; 6 | 7 | switch (type) { 8 | case 'circle': { 9 | let center = overlay.getCenter(); 10 | let radius = overlay.radius; 11 | 12 | paths = [createCircle(center.lat(), center.lng(), radius)]; 13 | break; 14 | } 15 | 16 | case 'rectangle': { 17 | let bounds = overlay.getBounds(); 18 | let ne = bounds.getNorthEast(); 19 | let sw = bounds.getSouthWest(); 20 | let nw = new google.maps.LatLng(ne.lat(), sw.lng()); 21 | let se = new google.maps.LatLng(sw.lat(), ne.lng()); 22 | let path = [ne, se, sw, nw]; 23 | 24 | paths = [path]; 25 | break; 26 | } 27 | } 28 | 29 | let polygon = new google.maps.Data.Polygon(paths); 30 | let feature = createFeature(polygon); 31 | 32 | return feature; 33 | } 34 | -------------------------------------------------------------------------------- /addon/utils/path-distance.js: -------------------------------------------------------------------------------- 1 | export default function pathDistance(points) { 2 | let processed = []; 3 | let value = points.reduce((val, point) => { 4 | let lastIndex = processed.length ? processed.length - 1 : undefined; 5 | 6 | if (lastIndex !== undefined) { 7 | let last = processed[lastIndex]; 8 | val += google.maps.geometry.spherical.computeDistanceBetween(last, point); 9 | } 10 | 11 | processed.push(point); 12 | 13 | return val; 14 | }, 0); 15 | 16 | return value; 17 | } 18 | -------------------------------------------------------------------------------- /addon/utils/paths-to-bounds.js: -------------------------------------------------------------------------------- 1 | export default function pathsToBounds(paths) { 2 | let bounds = new google.maps.LatLngBounds(); 3 | 4 | paths.forEach((latlng) => { 5 | bounds.extend(latlng); 6 | }); 7 | 8 | return bounds; 9 | } 10 | -------------------------------------------------------------------------------- /addon/utils/shape-area.js: -------------------------------------------------------------------------------- 1 | export function polygonArea(polygon) { 2 | let geometry = polygon.getGeometry(); 3 | 4 | return google.maps.geometry.spherical.computeArea( 5 | geometry.getArray()[0].getArray() 6 | ); 7 | } 8 | 9 | export function circleArea(circle) { 10 | let geometry = circle.getGeometry(); 11 | 12 | return google.maps.geometry.spherical.computeArea( 13 | geometry.getArray()[0].getArray() 14 | ); 15 | } 16 | 17 | export function rectangleArea(rectangle) { 18 | let geometry = rectangle.getGeometry(); 19 | 20 | return google.maps.geometry.spherical.computeArea( 21 | geometry.getArray()[0].getArray() 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /addon/utils/tools.js: -------------------------------------------------------------------------------- 1 | export default function getTools() { 2 | return { 3 | pan: { 4 | id: 'pan', 5 | name: 'Pan', 6 | title: 'Pan Tool', 7 | }, 8 | text: { 9 | id: 'text', 10 | name: 'Text', 11 | title: 'Text Label Tool', 12 | options: [ 13 | { name: 'Font Size', type: 'size', id: 'style.fontSize' }, 14 | { name: 'Color', type: 'color', id: 'style.color' }, 15 | ], 16 | style: { 17 | color: '#374046', 18 | fontSize: '12', 19 | }, 20 | }, 21 | marker: { 22 | id: 'marker', 23 | dataId: 'Point', 24 | name: 'Marker', 25 | title: 'Marker Tool', 26 | }, 27 | polyline: { 28 | id: 'polyline', 29 | dataId: 'LineString', 30 | name: 'Line', 31 | title: 'Multi segmented line tool', 32 | options: [ 33 | { name: 'Distance Unit', type: 'distanceUnit', id: 'distanceUnit' }, 34 | { name: 'Line Width', type: 'width', id: 'style.strokeWeight' }, 35 | { name: 'Color', type: 'color', id: 'style.strokeColor' }, 36 | ], 37 | style: { 38 | strokeColor: '#374046', 39 | strokeWeight: 2, 40 | }, 41 | distanceUnitId: 'ft', 42 | }, 43 | polygon: { 44 | id: 'polygon', 45 | dataId: 'Polygon', 46 | name: 'Polygon', 47 | title: 'Polygon Tool', 48 | options: [ 49 | { name: 'Area Unit', type: 'distanceUnit', id: 'distanceUnit' }, 50 | { name: 'Line Width', type: 'width', id: 'style.strokeWeight' }, 51 | { 52 | name: 'Fill Color', 53 | type: 'color', 54 | id: 'style.fillColor', 55 | fillOptional: true, 56 | }, 57 | { name: 'Stroke Color', type: 'color', id: 'style.strokeColor' }, 58 | { name: 'Fill Opacity', type: 'opacity', id: 'style.fillOpacity' }, 59 | ], 60 | style: { 61 | strokeColor: '#374046', 62 | fillColor: '#374046', 63 | strokeWeight: 2, 64 | fillOpacity: 0.5, 65 | }, 66 | fillColorTransparent: true, 67 | distanceUnitId: 'sq ft', 68 | }, 69 | freeFormPolygon: { 70 | id: 'freeFormPolygon', 71 | name: 'Freeform Polygon', 72 | title: 'Freeform Polygon Tool', 73 | options: [ 74 | { name: 'Line Width', type: 'width', id: 'style.strokeWeight' }, 75 | { 76 | name: 'Fill Color', 77 | type: 'color', 78 | id: 'style.fillColor', 79 | fillOptional: true, 80 | }, 81 | { name: 'Stroke Color', type: 'color', id: 'style.strokeColor' }, 82 | { name: 'Fill Opacity', type: 'opacity', id: 'style.fillOpacity' }, 83 | ], 84 | style: { 85 | strokeColor: '#374046', 86 | fillColor: '#374046', 87 | strokeWeight: 2, 88 | }, 89 | fillColorTransparent: true, 90 | distanceUnitId: 'sq ft', 91 | }, 92 | circle: { 93 | id: 'circle', 94 | dmId: google.maps.drawing.OverlayType.CIRCLE, 95 | name: 'Circle', 96 | title: 'Circle Tool', 97 | options: [ 98 | { name: 'Area Unit', type: 'distanceUnit', id: 'distanceUnit' }, 99 | { name: 'Line Width', type: 'width', id: 'style.strokeWeight' }, 100 | { 101 | name: 'Fill Color', 102 | type: 'color', 103 | id: 'style.fillColor', 104 | fillOptional: true, 105 | }, 106 | { name: 'Stroke Color', type: 'color', id: 'style.strokeColor' }, 107 | { name: 'Fill Opacity', type: 'opacity', id: 'style.fillOpacity' }, 108 | ], 109 | style: { 110 | strokeColor: '#374046', 111 | fillColor: '#374046', 112 | fillOpacity: 0.5, 113 | strokeWeight: 2, 114 | }, 115 | fillColorTransparent: true, 116 | distanceUnitId: 'sq ft', 117 | }, 118 | rectangle: { 119 | id: 'rectangle', 120 | dmId: google.maps.drawing.OverlayType.RECTANGLE, 121 | name: 'Rectangle', 122 | title: 'Rectangle Tool', 123 | options: [ 124 | { name: 'Area Unit', type: 'distanceUnit', id: 'distanceUnit' }, 125 | { name: 'Line Width', type: 'width', id: 'style.strokeWeight' }, 126 | { 127 | name: 'Fill Color', 128 | type: 'color', 129 | id: 'style.fillColor', 130 | fillOptional: true, 131 | }, 132 | { name: 'Stroke Color', type: 'color', id: 'style.strokeColor' }, 133 | { name: 'Fill Opacity', type: 'opacity', id: 'style.fillOpacity' }, 134 | ], 135 | style: { 136 | strokeColor: '#374046', 137 | fillColor: '#374046', 138 | fillOpacity: 0.5, 139 | strokeWeight: 2, 140 | }, 141 | fillColorTransparent: true, 142 | distanceUnitId: 'sq ft', 143 | }, 144 | }; 145 | } 146 | -------------------------------------------------------------------------------- /app/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knownasilya/google-maps-markup/27861c2eebe34bb73e91ce4ff45657576ac5d2d1/app/.gitkeep -------------------------------------------------------------------------------- /app/components/gmm-result-item.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/components/gmm-result-item'; 2 | -------------------------------------------------------------------------------- /app/components/gmm-sortable-results.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/components/gmm-sortable-results'; 2 | -------------------------------------------------------------------------------- /app/components/gmm-tool-options.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/components/gmm-tool-options'; 2 | -------------------------------------------------------------------------------- /app/components/google-maps-markup.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/components/google-maps-markup'; 2 | -------------------------------------------------------------------------------- /app/helpers/is-equal.js: -------------------------------------------------------------------------------- 1 | export { default, isEqual } from 'google-maps-markup/helpers/is-equal'; 2 | -------------------------------------------------------------------------------- /app/helpers/present.js: -------------------------------------------------------------------------------- 1 | export { default, present } from 'google-maps-markup/helpers/present'; 2 | -------------------------------------------------------------------------------- /app/helpers/tool-id.js: -------------------------------------------------------------------------------- 1 | export { default, toolId } from 'google-maps-markup/helpers/tool-id'; 2 | -------------------------------------------------------------------------------- /app/services/markup-data.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/services/markup-data'; -------------------------------------------------------------------------------- /app/styles/app.less: -------------------------------------------------------------------------------- 1 | 2 | @import "ember-power-select"; 3 | -------------------------------------------------------------------------------- /app/utils/create-circle.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/create-circle'; -------------------------------------------------------------------------------- /app/utils/create-feature.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/create-feature'; -------------------------------------------------------------------------------- /app/utils/distance.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/distance'; -------------------------------------------------------------------------------- /app/utils/feature-center.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/feature-center'; -------------------------------------------------------------------------------- /app/utils/format-number.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/format-number'; 2 | -------------------------------------------------------------------------------- /app/utils/get-measurement.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/get-measurement'; -------------------------------------------------------------------------------- /app/utils/guid.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/guid'; -------------------------------------------------------------------------------- /app/utils/init-measure-label.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/init-measure-label'; -------------------------------------------------------------------------------- /app/utils/label-plotter.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/label-plotter'; -------------------------------------------------------------------------------- /app/utils/map-label.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/map-label'; -------------------------------------------------------------------------------- /app/utils/measure-type-result.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/measure-type-result'; -------------------------------------------------------------------------------- /app/utils/modes.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/modes'; -------------------------------------------------------------------------------- /app/utils/number-commas.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/number-commas'; -------------------------------------------------------------------------------- /app/utils/options-data.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/options-data'; 2 | -------------------------------------------------------------------------------- /app/utils/overlay-to-feature.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/overlay-to-feature'; -------------------------------------------------------------------------------- /app/utils/path-distance.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/path-distance'; -------------------------------------------------------------------------------- /app/utils/paths-to-bounds.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/paths-to-bounds'; -------------------------------------------------------------------------------- /app/utils/shape-area.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/shape-area'; -------------------------------------------------------------------------------- /app/utils/square-miles.js: -------------------------------------------------------------------------------- 1 | export { default } from 'google-maps-markup/utils/square-miles'; -------------------------------------------------------------------------------- /codemods.log: -------------------------------------------------------------------------------- 1 | 2020-09-17T20:54:32.485Z [warn] [addon/utils/path-distance.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 2 | 2020-09-17T20:54:32.497Z [warn] [addon/utils/create-circle.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 3 | 2020-09-17T20:54:32.523Z [warn] [addon/utils/init-text-label.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 4 | 2020-09-17T20:54:32.555Z [warn] [addon/utils/paths-to-bounds.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 5 | 2020-09-17T20:54:32.573Z [warn] [addon/utils/init-measure-label.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 6 | 2020-09-17T20:54:32.579Z [warn] [addon/utils/create-feature.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 7 | 2020-09-17T20:54:32.711Z [warn] [addon/helpers/is-equal.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 8 | 2020-09-17T20:54:32.804Z [warn] [addon/helpers/tool-id.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 9 | 2020-09-17T20:54:50.804Z [warn] [addon/utils/modes.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 10 | 2020-09-17T20:54:53.704Z [warn] [addon/utils/number-commas.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 11 | 2020-09-17T20:54:55.969Z [warn] [addon/utils/layer.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 12 | 2020-09-17T20:55:00.974Z [warn] [addon/utils/distance.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 13 | 2020-09-17T20:55:01.114Z [info] [addon/components/gmm-markup-results/component.js]: SUCCESS 14 | 2020-09-17T20:55:01.130Z [warn] [addon/utils/guid.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 15 | 2020-09-17T20:55:01.245Z [warn] [addon/utils/options-data.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 16 | 2020-09-17T20:55:01.438Z [warn] [addon/utils/feature-center.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 17 | 2020-09-17T20:55:01.743Z [info] [addon/components/gmm-tool-options/component.js]: SUCCESS 18 | 2020-09-17T20:55:01.779Z [warn] [addon/utils/shape-area.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 19 | 2020-09-17T20:55:01.803Z [warn] [addon/utils/format-number.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 20 | 2020-09-17T20:55:01.806Z [warn] [addon/utils/get-measurement.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 21 | 2020-09-17T20:55:02.049Z [warn] [addon/utils/label-plotter.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 22 | 2020-09-17T20:55:02.097Z [warn] [addon/utils/overlay-to-feature.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 23 | 2020-09-17T20:55:02.555Z [warn] [addon/utils/map-label.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 24 | 2020-09-17T20:55:02.581Z [warn] [addon/utils/dynamic-label.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 25 | 2020-09-17T20:55:02.587Z [warn] [addon/utils/tools.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 26 | 2020-09-17T20:55:02.667Z [warn] [addon/services/markup-data.js]: FAILURE 27 | Validation errors: 28 | [markupResults]: Transform not supported - need option '--decorators=true' or the property type CallExpression can not be transformed 29 | [markupResults]: Transform not supported - value has modifiers like 'property' or 'meta' 30 | [textGeoJson]: Transform not supported - need option '--decorators=true' or the property type CallExpression can not be transformed 31 | 2020-09-17T20:55:02.732Z [warn] [addon/utils/measure-type-result.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 32 | 2020-09-17T20:55:02.782Z [warn] [addon/utils/area.js]: SKIPPED Could not find runtime data NO_RUNTIME_DATA 33 | 2020-09-17T20:55:04.284Z [warn] [addon/components/google-maps-markup/component.js]: FAILURE 34 | Validation errors: 35 | [listeners]: Transform not supported - need option '--decorators=true' or the property type CallExpression can not be transformed 36 | [toolListeners]: Transform not supported - need option '--decorators=true' or the property type CallExpression can not be transformed 37 | [currentPoints]: Transform not supported - need option '--decorators=true' or the property type CallExpression can not be transformed 38 | [drawingTools]: Transform not supported - need option '--decorators=true' or the property type CallExpression can not be transformed 39 | [measureTools]: Transform not supported - need option '--decorators=true' or the property type CallExpression can not be transformed 40 | [fillColorTransparent]: Transform not supported - calling the passed action would cause an infinite loop. See https://github.com/scalvert/eslint-plugin-ember-es6-class/pull/2 for more details 41 | 2020-09-17T20:55:05.032Z [info] [addon/components/markup-result-item/component.js]: SUCCESS 42 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChannelURL = require('ember-source-channel-url'); 4 | const { embroiderSafe, embroiderOptimized } = require('@embroider/test-setup'); 5 | 6 | module.exports = async function () { 7 | return { 8 | scenarios: [ 9 | { 10 | name: 'ember-lts-3.16', 11 | npm: { 12 | devDependencies: { 13 | 'ember-source': '~3.16.0', 14 | }, 15 | }, 16 | }, 17 | { 18 | name: 'ember-lts-3.20', 19 | npm: { 20 | devDependencies: { 21 | 'ember-source': '~3.20.5', 22 | }, 23 | }, 24 | }, 25 | { 26 | name: 'ember-release', 27 | npm: { 28 | devDependencies: { 29 | 'ember-source': await getChannelURL('release'), 30 | }, 31 | }, 32 | }, 33 | { 34 | name: 'ember-beta', 35 | npm: { 36 | devDependencies: { 37 | 'ember-source': await getChannelURL('beta'), 38 | }, 39 | }, 40 | }, 41 | { 42 | name: 'ember-canary', 43 | npm: { 44 | devDependencies: { 45 | 'ember-source': await getChannelURL('canary'), 46 | }, 47 | }, 48 | }, 49 | { 50 | name: 'ember-default-with-jquery', 51 | env: { 52 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 53 | 'jquery-integration': true, 54 | }), 55 | }, 56 | npm: { 57 | devDependencies: { 58 | '@ember/jquery': '^1.1.0', 59 | }, 60 | }, 61 | }, 62 | { 63 | name: 'ember-classic', 64 | env: { 65 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 66 | 'application-template-wrapper': true, 67 | 'default-async-observers': false, 68 | 'template-only-glimmer-components': false, 69 | }), 70 | }, 71 | npm: { 72 | ember: { 73 | edition: 'classic', 74 | }, 75 | }, 76 | }, 77 | embroiderSafe(), 78 | embroiderOptimized(), 79 | ], 80 | }; 81 | }; 82 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (/* environment, appConfig */) { 4 | return {}; 5 | }; 6 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const mergeTrees = require('broccoli-merge-trees'); 5 | const funnel = require('broccoli-funnel'); 6 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 7 | 8 | module.exports = function (defaults) { 9 | let app = new EmberAddon(defaults, { 10 | // Add options here 11 | }); 12 | 13 | /* 14 | This build file specifies the options for the dummy test app of this 15 | addon, located in `/tests/dummy` 16 | This build file does *not* influence how the addon or the app using it 17 | behave. You most likely want to be modifying `./index.js` or app's build file 18 | */ 19 | 20 | let fontsDir = path.dirname(require.resolve('font-awesome/package.json')); 21 | let fonts = funnel(fontsDir, { 22 | srcDir: 'fonts', 23 | destDir: 'assets/fonts', 24 | }); 25 | 26 | const { maybeEmbroider } = require('@embroider/test-setup'); 27 | return mergeTrees([maybeEmbroider(app), fonts]); 28 | }; 29 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | name: require('./package').name, 5 | 6 | included() { 7 | this._super.included.apply(this, arguments); 8 | 9 | this.import('vendor/google-maps-markup/styles.css'); 10 | }, 11 | 12 | isDevelopingAddon() { 13 | return process.env.DEVELOPING === 'true'; 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | {"compilerOptions":{"target":"es6","experimentalDecorators":true},"exclude":["node_modules","tmp","vendor",".git","dist"]} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-maps-markup", 3 | "version": "5.0.0", 4 | "description": "Ember markup component for Google Maps - Draw and Measure", 5 | "keywords": [ 6 | "ember-addon", 7 | "google maps", 8 | "markup", 9 | "draw", 10 | "measure" 11 | ], 12 | "license": "ISC", 13 | "author": "Ilya Radchenko", 14 | "directories": { 15 | "doc": "doc", 16 | "test": "tests" 17 | }, 18 | "repository": "git@github.com:knownasilya/google-maps-markup.git", 19 | "scripts": { 20 | "build": "ember build --environment=production", 21 | "lint": "npm-run-all --aggregate-output --continue-on-error --parallel 'lint:!(fix)'", 22 | "lint:fix": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*:fix", 23 | "lint:hbs": "ember-template-lint .", 24 | "lint:hbs:fix": "ember-template-lint . --fix", 25 | "lint:js": "eslint . --cache", 26 | "lint:js:fix": "eslint . --fix", 27 | "start": "ember serve", 28 | "test": "npm-run-all lint test:*", 29 | "test:ember": "ember test", 30 | "test:ember-compatibility": "ember try:each", 31 | "pretest": "mkdir coverage", 32 | "release": "standard-version" 33 | }, 34 | "dependencies": { 35 | "@ember/render-modifiers": "^1.0.2", 36 | "@glimmer/component": "^1.0.4", 37 | "@glimmer/tracking": "^1.0.4", 38 | "ember-auto-import": "^1.11.3", 39 | "ember-cli-babel": "^7.26.5", 40 | "ember-cli-htmlbars": "^5.7.1", 41 | "ember-composability-tools": "^1.0.1", 42 | "ember-copy": "^2.0.1", 43 | "ember-drag-drop": "0.9.0-beta.0", 44 | "ember-palette": "^3.0.2", 45 | "ember-power-select": "^4.1.5", 46 | "ember-wormhole": "^0.6.0", 47 | "uuid": "^8.3.2" 48 | }, 49 | "devDependencies": { 50 | "@ember/optional-features": "^2.0.0", 51 | "@ember/test-helpers": "^2.2.5", 52 | "@embroider/test-setup": "^0.40.0", 53 | "babel-eslint": "^10.1.0", 54 | "bootstrap": "^3.4.1", 55 | "broccoli-asset-rev": "^3.0.0", 56 | "broccoli-funnel": "^3.0.5", 57 | "broccoli-merge-trees": "^4.2.0", 58 | "coveralls": "^3.1.0", 59 | "ember-cached-decorator-polyfill": "^0.1.1", 60 | "ember-cli": "~3.26.1", 61 | "ember-cli-code-coverage": "^1.0.3", 62 | "ember-cli-dependency-checker": "^3.2.0", 63 | "ember-cli-github-pages": "^0.2.2", 64 | "ember-cli-inject-live-reload": "^2.0.2", 65 | "ember-cli-less": "^3.0.1", 66 | "ember-cli-sri": "^2.1.1", 67 | "ember-cli-terser": "^4.0.1", 68 | "ember-disable-prototype-extensions": "^1.1.3", 69 | "ember-export-application-global": "^2.0.1", 70 | "ember-load-initializers": "^2.1.2", 71 | "ember-page-title": "^6.2.1", 72 | "ember-qunit": "^5.1.4", 73 | "ember-resolver": "^8.0.2", 74 | "ember-source": "~3.26.1", 75 | "ember-source-channel-url": "^3.0.0", 76 | "ember-template-lint": "^3.3.1", 77 | "ember-try": "^1.4.0", 78 | "eslint": "^7.25.0", 79 | "eslint-config-prettier": "^8.3.0", 80 | "eslint-plugin-ember": "^10.4.1", 81 | "eslint-plugin-node": "^11.1.0", 82 | "eslint-plugin-prettier": "^3.4.0", 83 | "font-awesome": "^4.7.0", 84 | "loader.js": "^4.7.0", 85 | "npm-run-all": "^4.1.5", 86 | "prettier": "^2.2.1", 87 | "qunit": "^2.15.0", 88 | "qunit-dom": "^1.6.0", 89 | "standard-version": "^9.3.0" 90 | }, 91 | "engines": { 92 | "node": ">= 12" 93 | }, 94 | "ember": { 95 | "edition": "octane" 96 | }, 97 | "ember-addon": { 98 | "configPath": "tests/dummy/config", 99 | "demoURL": "http://knownasilya.github.io/google-maps-markup/" 100 | }, 101 | "volta": { 102 | "node": "12.22.1" 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /public/images/spotlight-poi-highlighted_hdpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knownasilya/google-maps-markup/27861c2eebe34bb73e91ce4ff45657576ac5d2d1/public/images/spotlight-poi-highlighted_hdpi.png -------------------------------------------------------------------------------- /public/netlify.toml: -------------------------------------------------------------------------------- 1 | # public/netlify.toml 2 | [[redirects]] 3 | from = "/*" 4 | to = "/index.html" 5 | status = 200 6 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | test_page: 'tests/index.html?hidepassed', 5 | disable_watching: true, 6 | launch_in_ci: ['Chrome'], 7 | launch_in_dev: ['Chrome'], 8 | browser_start_timeout: 120, 9 | browser_args: { 10 | Chrome: { 11 | ci: [ 12 | // --no-sandbox is needed when running Chrome inside a container 13 | process.env.CI ? '--no-sandbox' : null, 14 | '--headless', 15 | '--disable-dev-shm-usage', 16 | '--disable-software-rasterizer', 17 | '--mute-audio', 18 | '--remote-debugging-port=0', 19 | '--window-size=1440,900', 20 | ].filter(Boolean), 21 | }, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from 'ember-resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from 'dummy/config/environment'; 5 | 6 | export default class App extends Application { 7 | modulePrefix = config.modulePrefix; 8 | podModulePrefix = config.podModulePrefix; 9 | Resolver = Resolver; 10 | } 11 | 12 | loadInitializers(App, config.modulePrefix); 13 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knownasilya/google-maps-markup/27861c2eebe34bb73e91ce4ff45657576ac5d2d1/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/components/google-map/component.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import { action } from '@ember/object'; 3 | import { next } from '@ember/runloop'; 4 | 5 | export default class GoogleMap extends Component { 6 | get center() { 7 | return ( 8 | this.args.center ?? 9 | new google.maps.LatLng(42.43540000000001, -71.11295997924805) 10 | ); 11 | } 12 | 13 | get zoom() { 14 | return this.args.zoom ?? 10; 15 | } 16 | 17 | @action 18 | setupMap(el) { 19 | let center = this.center; 20 | let zoom = this.zoom; 21 | let options = { 22 | zoom, 23 | center, 24 | maxZoom: 19, 25 | gestureHandling: 'greedy', 26 | }; 27 | 28 | google.maps.visualRefresh = true; 29 | let map = new google.maps.Map(el, options); 30 | 31 | // debugging 32 | window.gmap = map; 33 | 34 | next(() => { 35 | this.args.onLoaded(map); 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/dummy/app/components/google-map/template.hbs: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knownasilya/google-maps-markup/27861c2eebe34bb73e91ce4ff45657576ac5d2d1/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/application.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | import { inject as service } from '@ember/service'; 3 | import { action } from '@ember/object'; 4 | import { all } from 'rsvp'; 5 | import { tracked } from '@glimmer/tracking'; 6 | 7 | export default class ApplicationController extends Controller { 8 | @service('markupData') markupDataService; 9 | @tracked exported; 10 | @tracked map; 11 | 12 | async exportMarkup() { 13 | let dataLayers = this.markupDataService.layers; 14 | 15 | if (dataLayers) { 16 | let promises = dataLayers.map((layer) => { 17 | return layer.toGeoJson(); 18 | }); 19 | 20 | let layers = await all(promises); 21 | 22 | // make sure empty feature collections aren't exported 23 | this.exported = layers; 24 | 25 | return layers; 26 | } else { 27 | return; 28 | } 29 | } 30 | 31 | loadMarkup(markupData) { 32 | let markupService = this.markupDataService; 33 | let dataLayers = markupService.layers; 34 | 35 | dataLayers.forEach((layer, index) => { 36 | layer.data.addGeoJson(markupData[index]); 37 | layer.data.forEach((feature) => 38 | markupService.featureToResult(feature, layer) 39 | ); 40 | }); 41 | 42 | markupService.changeModeByResults(); 43 | } 44 | 45 | @action 46 | load() { 47 | let exported = this.exported; 48 | 49 | if (exported) { 50 | this.loadMarkup(exported); 51 | } 52 | } 53 | 54 | @action 55 | save() { 56 | this.exportMarkup(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knownasilya/google-maps-markup/27861c2eebe34bb73e91ce4ff45657576ac5d2d1/tests/dummy/app/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | google-maps-markup demo 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | 16 | {{content-for "head-footer"}} 17 | 18 | 19 | {{content-for "body"}} 20 | 21 | 22 | 23 | 24 |
25 | {{content-for "body-footer"}} 26 | 27 | 28 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knownasilya/google-maps-markup/27861c2eebe34bb73e91ce4ff45657576ac5d2d1/tests/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from 'dummy/config/environment'; 3 | 4 | export default class Router extends EmberRouter { 5 | location = config.locationType; 6 | rootURL = config.rootURL; 7 | } 8 | 9 | Router.map(function () {}); 10 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knownasilya/google-maps-markup/27861c2eebe34bb73e91ce4ff45657576ac5d2d1/tests/dummy/app/routes/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.less: -------------------------------------------------------------------------------- 1 | @import "node_modules/bootstrap/less/bootstrap.less"; 2 | @import "node_modules/font-awesome/less/font-awesome.less"; 3 | @import "ember-power-select/themes/bootstrap"; 4 | @import "ember-power-select"; 5 | 6 | @fa-font-path: "./fonts"; 7 | 8 | #map { 9 | position: absolute !important; 10 | top: 10px; 11 | right: 10px; 12 | width: 100%; 13 | height: 400px; 14 | } 15 | 16 | #title { 17 | margin-left: 0.5em; 18 | } 19 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | {{page-title 'Google Maps Markup'}} 2 | 3 |

4 | google-maps-markup 5 |

6 | 7 |
8 |
9 |
10 | 16 |
17 | 20 | {{#if this.exported}} 21 | 24 | {{/if}} 25 |
26 |
27 | 28 |
29 |
30 |
31 | 32 | {{outlet}} -------------------------------------------------------------------------------- /tests/dummy/config/ember-cli-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "packages": [ 4 | { 5 | "name": "ember-cli", 6 | "version": "3.26.1", 7 | "blueprints": [ 8 | { 9 | "name": "addon", 10 | "outputRepo": "https://github.com/ember-cli/ember-addon-output", 11 | "codemodsSource": "ember-addon-codemods-manifest@1", 12 | "isBaseBlueprint": true, 13 | "options": [ 14 | "--no-welcome" 15 | ] 16 | } 17 | ] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (environment) { 4 | let ENV = { 5 | modulePrefix: 'dummy', 6 | environment, 7 | rootURL: '/', 8 | locationType: 'auto', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true 13 | }, 14 | EXTEND_PROTOTYPES: { 15 | // Prevent Ember Data from overriding Date.parse. 16 | Date: false, 17 | }, 18 | }, 19 | 20 | APP: { 21 | // Here you can pass flags/options to your application instance 22 | // when it is created 23 | }, 24 | }; 25 | 26 | if (environment === 'development') { 27 | // ENV.APP.LOG_RESOLVER = true; 28 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 29 | // ENV.APP.LOG_TRANSITIONS = true; 30 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 31 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 32 | } 33 | 34 | if (environment === 'test') { 35 | // Testem prefers this... 36 | ENV.locationType = 'none'; 37 | 38 | // keep test console output quieter 39 | ENV.APP.LOG_ACTIVE_GENERATION = false; 40 | ENV.APP.LOG_VIEW_LOOKUPS = false; 41 | 42 | ENV.APP.rootElement = '#ember-testing'; 43 | ENV.APP.autoboot = false; 44 | } 45 | 46 | if (environment === 'production') { 47 | // here you can enable a production-specific feature 48 | } 49 | 50 | return ENV; 51 | }; 52 | -------------------------------------------------------------------------------- /tests/dummy/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "application-template-wrapper": false, 3 | "default-async-observers": true, 4 | "jquery-integration": false, 5 | "template-only-glimmer-components": true 6 | } 7 | -------------------------------------------------------------------------------- /tests/dummy/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const browsers = [ 4 | 'last 1 Chrome versions', 5 | 'last 1 Firefox versions', 6 | 'last 1 Safari versions', 7 | ]; 8 | 9 | const isCI = Boolean(process.env.CI); 10 | const isProduction = process.env.EMBER_ENV === 'production'; 11 | 12 | if (isCI || isProduction) { 13 | browsers.push('ie 11'); 14 | } 15 | 16 | module.exports = { 17 | browsers, 18 | }; 19 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /tests/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knownasilya/google-maps-markup/27861c2eebe34bb73e91ce4ff45657576ac5d2d1/tests/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy Tests 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | {{content-for "test-head"}} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {{content-for "head-footer"}} 20 | {{content-for "test-head-footer"}} 21 | 22 | 23 | {{content-for "body"}} 24 | {{content-for "test-body"}} 25 | 26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | {{content-for "body-footer"}} 42 | {{content-for "test-body-footer"}} 43 | 44 | 45 | -------------------------------------------------------------------------------- /tests/integration/components/gmm-result-item/component-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { render, find } from '@ember/test-helpers'; 4 | import hbs from 'htmlbars-inline-precompile'; 5 | 6 | module('Integration | Component | gmm result item', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('it renders', async function (assert) { 10 | assert.expect(1); 11 | 12 | // Set any properties with this.set('myProperty', 'value'); 13 | // Handle any actions with this.on('myAction', function(val) { ... }); 14 | this.set('data', { 15 | mode: 'draw', 16 | type: 'circle', 17 | }); 18 | 19 | await render(hbs``); 20 | 21 | let text = find('*') 22 | .textContent.trim() 23 | .split('\n') 24 | .join(' ') 25 | .replace(/\s/g, ''); 26 | 27 | assert.equal(text, 'circleEditRemove'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /tests/integration/components/gmm-sortable-results/component-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { render } from '@ember/test-helpers'; 4 | import hbs from 'htmlbars-inline-precompile'; 5 | 6 | module('Integration | Component | gmm sortable results', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('it renders', async function (assert) { 10 | assert.expect(1); 11 | 12 | // Set any properties with this.set('myProperty', 'value'); 13 | // Handle any actions with this.on('myAction', function(val) { ... }); 14 | 15 | await render(hbs``); 16 | 17 | assert.dom('*').hasText(''); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/integration/components/gmm-tool-options/component-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { render } from '@ember/test-helpers'; 4 | import hbs from 'htmlbars-inline-precompile'; 5 | 6 | module('Integration | Component | gmm tool options', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('it renders', async function (assert) { 10 | // Set any properties with this.set('myProperty', 'value'); 11 | // Handle any actions with this.on('myAction', function(val) { ... }); 12 | 13 | await render(hbs``); 14 | 15 | assert.dom('*').hasText(''); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /tests/integration/components/google-maps-markup/component-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { render } from '@ember/test-helpers'; 4 | import hbs from 'htmlbars-inline-precompile'; 5 | 6 | module('Integration | Component | google maps markup', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('it renders', async function (assert) { 10 | assert.expect(1); 11 | 12 | // Set any properties with this.set('myProperty', 'value'); 13 | // Handle any actions with this.on('myAction', function(val) { ... }); 14 | 15 | await render(hbs``); 16 | 17 | assert.equal(this.element.find('h4').first().text(), 'Mode'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import Application from 'dummy/app'; 2 | import config from 'dummy/config/environment'; 3 | import * as QUnit from 'qunit'; 4 | import { setApplication } from '@ember/test-helpers'; 5 | import { setup } from 'qunit-dom'; 6 | import { start } from 'ember-qunit'; 7 | 8 | setApplication(Application.create(config.APP)); 9 | 10 | setup(QUnit.assert); 11 | 12 | start(); 13 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knownasilya/google-maps-markup/27861c2eebe34bb73e91ce4ff45657576ac5d2d1/tests/unit/.gitkeep -------------------------------------------------------------------------------- /tests/unit/helpers/is-equal-test.js: -------------------------------------------------------------------------------- 1 | import { isEqual } from '../../../helpers/is-equal'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Helper | is equal', function () { 5 | test('it works for equality', function (assert) { 6 | let result = isEqual(42, 42); 7 | assert.ok(result); 8 | }); 9 | 10 | test('it works for inequality', function (assert) { 11 | let result = isEqual(42, 41); 12 | assert.ok(result); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /tests/unit/helpers/tool-id-test.js: -------------------------------------------------------------------------------- 1 | import { toolId } from 'dummy/helpers/tool-id'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Helper | tool id', function () { 5 | test('it works with id', function (assert) { 6 | let result = toolId([{ id: 'test' }]); 7 | assert.equal(result, 'test'); 8 | }); 9 | 10 | test('it works with type', function (assert) { 11 | let result = toolId([{ type: 'test' }]); 12 | assert.equal(result, 'test'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /tests/unit/services/markup-data-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupTest } from 'ember-qunit'; 3 | 4 | module('Unit | Service | markup data', function (hooks) { 5 | setupTest(hooks); 6 | 7 | // Replace this with your real tests. 8 | test('it exists', function (assert) { 9 | var service = this.owner.lookup('service:markup-data'); 10 | assert.ok(service); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /tests/unit/utils/area-test.js: -------------------------------------------------------------------------------- 1 | import area from 'google-maps-markup/utils/area'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | area', function () { 5 | test('over threshold - one level', function (assert) { 6 | let input = { 7 | value: 60000, 8 | unit: { 9 | id: 'sq ft', 10 | }, 11 | measurementType: 'Area', 12 | }; 13 | let result = area(input); 14 | 15 | assert.deepEqual(result, { 16 | value: 1.4, 17 | unit: 'acres', 18 | measurementType: input.measurementType, 19 | }); 20 | }); 21 | 22 | test('over threshold - two levels', function (assert) { 23 | let input = { 24 | value: 27980000, 25 | unit: { 26 | id: 'sq ft', 27 | }, 28 | measurementType: 'Area', 29 | }; 30 | let result = area(input); 31 | 32 | assert.deepEqual(result, { 33 | value: 1, 34 | unit: 'sq mi', 35 | measurementType: input.measurementType, 36 | }); 37 | }); 38 | 39 | test('below threshold', function (assert) { 40 | let input = { 41 | value: 2000, 42 | unit: { 43 | id: 'sq ft', 44 | }, 45 | measurementType: 'Area', 46 | }; 47 | let result = area(input); 48 | 49 | assert.deepEqual(result, { 50 | value: 2000, 51 | unit: 'sq ft', 52 | measurementType: input.measurementType, 53 | }); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /tests/unit/utils/create-circle-test.js: -------------------------------------------------------------------------------- 1 | import createCircle from '../../../utils/create-circle'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | create circle', function () { 5 | // Replace this with your real tests. 6 | test('it works', function (assert) { 7 | var lat = 42.48019996901214; 8 | var lng = -71.10763549804688; 9 | var radius = 3947.236927961412; 10 | var result = createCircle(lat, lng, radius); 11 | 12 | assert.equal(result.length, 33); 13 | assert.ok(result[0].lat()); 14 | assert.ok(result[0].lng()); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /tests/unit/utils/create-feature-test.js: -------------------------------------------------------------------------------- 1 | import createFeature from '../../../utils/create-feature'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | create feature', function () { 5 | // Replace this with your real tests. 6 | test('it works', function (assert) { 7 | var result = createFeature(); 8 | assert.ok(result); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/unit/utils/distance-test.js: -------------------------------------------------------------------------------- 1 | import distance from '../../../utils/distance'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | miles', function () { 5 | // Replace this with your real tests. 6 | test('it works', function (assert) { 7 | var input = { 8 | value: 2000, 9 | unit: 'ft.', 10 | measurementType: 'Distance', 11 | }; 12 | var result = distance(input); 13 | 14 | assert.deepEqual(result, { 15 | value: 0.3788, 16 | unit: 'mi.', 17 | measurementType: input.measurementType, 18 | }); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /tests/unit/utils/feature-center-test.js: -------------------------------------------------------------------------------- 1 | import featureCenter from '../../../utils/feature-center'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | feature center', function () { 5 | test('point', function (assert) { 6 | var feature = new google.maps.Data.Feature({ 7 | geometry: new google.maps.Data.Point({ 8 | lat: 40.509622849596695, 9 | lng: -75.5474853515625, 10 | }), 11 | }); 12 | var result = featureCenter(feature); 13 | 14 | assert.equal(result.lat(), 40.509622849596695); 15 | assert.equal(result.lng(), -75.5474853515625); 16 | }); 17 | 18 | test('polygon', function (assert) { 19 | var feature = new google.maps.Data.Feature({ 20 | geometry: new google.maps.Data.Polygon([ 21 | [ 22 | { lat: 42.52677220056902, lng: -71.114501953125 }, 23 | { lat: 42.4923525914282, lng: -71.14471435546875 }, 24 | { lat: 42.49336520339777, lng: -71.06369018554688 }, 25 | ], 26 | ]), 27 | }); 28 | var result = featureCenter(feature); 29 | 30 | assert.equal(result.lat(), 42.509562395998614); 31 | assert.equal(result.lng(), -71.10420227050781); 32 | }); 33 | 34 | test('linestring', function (assert) { 35 | var feature = new google.maps.Data.Feature({ 36 | geometry: new google.maps.Data.LineString([ 37 | { lat: 42.5338562378485, lng: -71.15982055664062 }, 38 | { lat: 42.5338562378485, lng: -71.05545043945312 }, 39 | { lat: 42.503490421367296, lng: -71.08291625976562 }, 40 | ]), 41 | }); 42 | var result = featureCenter(feature); 43 | 44 | assert.equal(result.lat(), 42.51867332960789); 45 | assert.equal(result.lng(), -71.10763549804688); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /tests/unit/utils/format-number-test.js: -------------------------------------------------------------------------------- 1 | import formatNumber from 'dummy/utils/format-number'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | format-number', function () { 5 | // Replace this with your real tests. 6 | test('it works', function (assert) { 7 | let result = formatNumber(); 8 | assert.ok(result); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/unit/utils/get-measurement-test.js: -------------------------------------------------------------------------------- 1 | import getMeasurement from '../../../utils/get-measurement'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | get measurement', function () { 5 | test('polyline', function (assert) { 6 | var feature = new google.maps.Data.Feature({ 7 | geometry: new google.maps.Data.LineString([ 8 | { lat: 42.5338562378485, lng: -71.15982055664062 }, 9 | { lat: 42.5338562378485, lng: -71.05545043945312 }, 10 | { lat: 42.503490421367296, lng: -71.08291625976562 }, 11 | ]), 12 | }); 13 | var result = getMeasurement('polyline', feature); 14 | 15 | assert.ok(result.value); 16 | assert.ok(result.unit); 17 | assert.ok(result.measurementType); 18 | }); 19 | 20 | test('polygon', function (assert) { 21 | var feature = new google.maps.Data.Feature({ 22 | geometry: new google.maps.Data.Polygon([ 23 | [ 24 | { lat: 42.52677220056902, lng: -71.114501953125 }, 25 | { lat: 42.4923525914282, lng: -71.14471435546875 }, 26 | { lat: 42.49336520339777, lng: -71.06369018554688 }, 27 | ], 28 | ]), 29 | }); 30 | var result = getMeasurement('polygon', feature); 31 | 32 | assert.ok(result.value); 33 | assert.ok(result.unit); 34 | assert.ok(result.measurementType); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /tests/unit/utils/guid-test.js: -------------------------------------------------------------------------------- 1 | import guid from '../../../utils/guid'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | guid', function () { 5 | // Replace this with your real tests. 6 | test('it works', function (assert) { 7 | var result = guid(); 8 | assert.ok(result); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/unit/utils/init-measure-label-test.js: -------------------------------------------------------------------------------- 1 | import initMeasureLabel from '../../../utils/init-measure-label'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | init measure label', function () { 5 | // Replace this with your real tests. 6 | test('it works', function (assert) { 7 | var result = initMeasureLabel(); 8 | assert.ok(result); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/unit/utils/label-plotter-test.js: -------------------------------------------------------------------------------- 1 | import labelPlotter from '../../../utils/label-plotter'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | label plotter', function () { 5 | // Replace this with your real tests. 6 | test('it works', function (assert) { 7 | var result = labelPlotter(); 8 | assert.ok(result); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/unit/utils/map-label-test.js: -------------------------------------------------------------------------------- 1 | import mapLabel from '../../../utils/map-label'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | map label', function () { 5 | // Replace this with your real tests. 6 | test('it works', function (assert) { 7 | var result = mapLabel(); 8 | assert.ok(result); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/unit/utils/measure-type-result-test.js: -------------------------------------------------------------------------------- 1 | import measureTypeResult from '../../../utils/measure-type-result'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | measure type result', function () { 5 | // Replace this with your real tests. 6 | test('it works', function (assert) { 7 | var result = measureTypeResult(); 8 | assert.ok(result); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/unit/utils/modes-test.js: -------------------------------------------------------------------------------- 1 | import modes from '../../../utils/modes'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | modes', function () { 5 | // Replace this with your real tests. 6 | test('it works', function (assert) { 7 | var result = modes; 8 | assert.ok(Object.keys(result)); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/unit/utils/number-commas-test.js: -------------------------------------------------------------------------------- 1 | import numberCommas from '../../../utils/number-commas'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | number commas', function () { 5 | // Replace this with your real tests. 6 | test('it works', function (assert) { 7 | var result = numberCommas(1234); 8 | assert.equal(result, '1,234'); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/unit/utils/options-data-test.js: -------------------------------------------------------------------------------- 1 | import optionsData from 'dummy/utils/options-data'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | options data', function () { 5 | // Replace this with your real tests. 6 | test('it works', function (assert) { 7 | let result = optionsData(); 8 | assert.ok(result); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/unit/utils/overlay-to-feature-test.js: -------------------------------------------------------------------------------- 1 | import overlayToFeature from '../../../utils/overlay-to-feature'; 2 | import { module, test } from 'qunit'; 3 | 4 | const LatLng = google.maps.LatLng; 5 | 6 | module('Unit | Utility | overlay to feature', function () { 7 | // Replace this with your real tests. 8 | test('it works', function (assert) { 9 | var circleOverlay = { 10 | radius: 3947.236927961412, 11 | 12 | getCenter() { 13 | return new LatLng(42.48019996901214, -71.10763549804688); 14 | }, 15 | }; 16 | 17 | var rectangleOverlay = { 18 | getBounds() { 19 | return { 20 | getNorthEast() { 21 | return new LatLng(42.532844281713125, -71.02523803710938); 22 | }, 23 | getSouthWest() { 24 | return new LatLng(42.46095348879495, -71.19827270507812); 25 | }, 26 | }; 27 | }, 28 | }; 29 | 30 | var circle = overlayToFeature('circle', circleOverlay); 31 | assert.ok(circle.getGeometry()); 32 | 33 | var rectangle = overlayToFeature('rectangle', rectangleOverlay); 34 | assert.ok(rectangle.getGeometry()); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /tests/unit/utils/path-distance-test.js: -------------------------------------------------------------------------------- 1 | import pathDistance from '../../../utils/path-distance'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | path distance', function () { 5 | // Replace this with your real tests. 6 | test('it works', function (assert) { 7 | var result = pathDistance(); 8 | assert.ok(result); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/unit/utils/paths-to-bounds-test.js: -------------------------------------------------------------------------------- 1 | import pathsToBounds from '../../../utils/paths-to-bounds'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | paths to bounds', function () { 5 | // Replace this with your real tests. 6 | test('it works', function (assert) { 7 | var result = pathsToBounds(); 8 | assert.ok(result); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/unit/utils/shape-area-test.js: -------------------------------------------------------------------------------- 1 | import { polygonArea } from 'google-maps-markup/utils/shape-area'; 2 | import { module, test } from 'qunit'; 3 | 4 | module('Unit | Utility | shape area', function () { 5 | // Replace this with your real tests. 6 | test('polygon', function (assert) { 7 | var feature = new google.maps.Data.Feature({ 8 | geometry: new google.maps.Data.Polygon([ 9 | [ 10 | { lat: 40.509622849596695, lng: -75.5474853515625 }, 11 | { lat: 40.691051628010236, lng: -75.5474853515625 }, 12 | { lat: 40.691051628010236, lng: -75.23712158203125 }, 13 | { lat: 40.509622849596695, lng: -75.23712158203125 }, 14 | ], 15 | ]), 16 | }); 17 | var result = polygonArea(feature); 18 | console.log(result); 19 | assert.ok(result); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knownasilya/google-maps-markup/27861c2eebe34bb73e91ce4ff45657576ac5d2d1/vendor/.gitkeep -------------------------------------------------------------------------------- /vendor/google-maps-markup/styles.css: -------------------------------------------------------------------------------- 1 | .google-maps-markup .results-list > li:hover { 2 | background-color: #fafafa; 3 | } 4 | 5 | .google-maps-markup .tool-options { 6 | display: grid; 7 | grid-template-columns: 1fr 1fr 1fr; 8 | gap: 1rem; 9 | grid-gap: 1rem; 10 | } 11 | 12 | .google-maps-markup .results-list .tool-options { 13 | margin: 9px -15px -10px -15px; 14 | padding: 8px 15px; 15 | background-color: #f5f5f5; 16 | } 17 | 18 | .google-maps-markup .tools-group .btn { 19 | margin-bottom: 0.4em; 20 | } 21 | 22 | .google-maps-markup-map-label, 23 | .google-maps-markup-map-hidden { 24 | color: #000; 25 | opacity: 1; 26 | font-size: 13px; 27 | font-weight: bold; 28 | line-height: 1.1em; 29 | text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 30 | 1px 1px 0 #fff; 31 | box-sizing: border-box; 32 | border: 1px solid transparent; 33 | padding: 0.1em; 34 | background-color: transparent; 35 | resize: none; 36 | overflow-y: hidden; 37 | } 38 | 39 | .google-maps-markup-map-label.highlighted { 40 | color: red !important; 41 | } 42 | 43 | .google-maps-markup-map-hidden { 44 | min-width: 100px; 45 | word-wrap: break-word; 46 | white-space: pre-wrap; 47 | visibility: hidden; 48 | display: inline-block; 49 | font-family: inherit; 50 | } 51 | 52 | .google-maps-markup-map-label.editing-text { 53 | min-width: 100px; 54 | border: 1px solid #333; 55 | } 56 | 57 | .google-maps-markup-map-label::-webkit-input-placeholder { 58 | /* WebKit, Blink, Edge */ 59 | color: #444; 60 | } 61 | .google-maps-markup-map-label::-moz-placeholder { 62 | /* Mozilla Firefox 19+ */ 63 | color: #444; 64 | opacity: 1; 65 | } 66 | .google-maps-markup-map-label:-ms-input-placeholder { 67 | /* Internet Explorer 10-11 */ 68 | color: #444; 69 | } 70 | .google-maps-markup-map-label:placeholder-shown { 71 | /* Standard (https://drafts.csswg.org/selectors-4/#placeholder) */ 72 | color: #444; 73 | } 74 | .google-maps-markup-map-label.set-width { 75 | max-width: 300px; 76 | height: auto; 77 | word-break: keep-all; 78 | border: none; 79 | } 80 | 81 | .google-maps-markup-map-label.set-width:focus { 82 | border: none; 83 | } 84 | 85 | .google-maps-markup-map-label.set-width .google-maps-markup-map-hidden { 86 | width: 100%; 87 | word-break: keep-all; 88 | } 89 | --------------------------------------------------------------------------------