diff options
author | Hsieh Chin Fan <pham@topo.tw> | 2024-11-03 13:58:38 +0800 |
---|---|---|
committer | Hsieh Chin Fan <pham@topo.tw> | 2024-11-03 14:36:15 +0800 |
commit | f4910b66e74034db14619ee384dc261423dc19fc (patch) | |
tree | 78382b3bb600b61a43b48d417cfbe3873d8da623 | |
parent | 3086cc484857dea32a33f829ec173adaf0cf8fbe (diff) |
feat: use bbox to update camera for result of Geocoding
* show markers from all Geocoding Results
* update mapclay for latest updateCamera method
* 3 steps for camera animation:
1. zoom out to bbox which contains all Geocoding Result
2. pan to marker of hovered result
3. zoom in to bbox from nonimatim query
* remove markers except selected Geocoding Result
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | src/Link.mjs | 2 | ||||
-rw-r--r-- | src/MenuItem.mjs | 65 | ||||
-rw-r--r-- | src/utils.mjs | 1 |
4 files changed, 53 insertions, 17 deletions
diff --git a/package.json b/package.json index a08a286..64ed496 100644 --- a/package.json +++ b/package.json | |||
@@ -44,7 +44,7 @@ | |||
44 | "cssprefix": "^2.0.17", | 44 | "cssprefix": "^2.0.17", |
45 | "easymde": "^2.18.0", | 45 | "easymde": "^2.18.0", |
46 | "leader-line": "^1.0.7", | 46 | "leader-line": "^1.0.7", |
47 | "mapclay": "^0.9.0", | 47 | "mapclay": "^0.9.2", |
48 | "markdown-it": "^14.1.0", | 48 | "markdown-it": "^14.1.0", |
49 | "markdown-it-anchor": "^9.2.0", | 49 | "markdown-it-anchor": "^9.2.0", |
50 | "markdown-it-footnote": "^4.0.0", | 50 | "markdown-it-footnote": "^4.0.0", |
diff --git a/src/Link.mjs b/src/Link.mjs index 7678f3a..685de3a 100644 --- a/src/Link.mjs +++ b/src/Link.mjs | |||
@@ -227,7 +227,7 @@ const isAnchorVisible = anchor => { | |||
227 | */ | 227 | */ |
228 | export const updateMapCameraByMarker = lonLat => marker => { | 228 | export const updateMapCameraByMarker = lonLat => marker => { |
229 | const renderer = marker.closest('.mapclay')?.renderer | 229 | const renderer = marker.closest('.mapclay')?.renderer |
230 | renderer.updateCamera({ center: lonLat }, true) | 230 | renderer?.updateCamera?.({ center: lonLat, animation: true }) |
231 | } | 231 | } |
232 | 232 | ||
233 | /** | 233 | /** |
diff --git a/src/MenuItem.mjs b/src/MenuItem.mjs index 6c6e6cf..8bee5c1 100644 --- a/src/MenuItem.mjs +++ b/src/MenuItem.mjs | |||
@@ -1,7 +1,7 @@ | |||
1 | import { shiftByWindow } from './utils.mjs' | 1 | import { onRemove, shiftByWindow } from './utils.mjs' |
2 | import { addMarkerByPoint } from './dumbyUtils.mjs' | 2 | import { addMarkerByPoint } from './dumbyUtils.mjs' |
3 | /* eslint-disable-next-line no-unused-vars */ | 3 | /* eslint-disable-next-line no-unused-vars */ |
4 | import { GeoLink, getMarkersFromMaps, getMarkersByGeoLink, removeLeaderLines, updateMapCameraByMarker } from './Link.mjs' | 4 | import { GeoLink, getMarkersFromMaps, getMarkersByGeoLink, removeLeaderLines } from './Link.mjs' |
5 | import * as markers from './marker.mjs' | 5 | import * as markers from './marker.mjs' |
6 | import { parseConfigsFromYaml } from 'mapclay' | 6 | import { parseConfigsFromYaml } from 'mapclay' |
7 | 7 | ||
@@ -606,8 +606,8 @@ export const addLinkbyGeocoding = (range) => { | |||
606 | /** Add spinning circle for Network */ | 606 | /** Add spinning circle for Network */ |
607 | e.target.classList.add('with-spinning-circle') | 607 | e.target.classList.add('with-spinning-circle') |
608 | const menu = e.target.closest('.dumby-menu') | 608 | const menu = e.target.closest('.dumby-menu') |
609 | |||
610 | if (!menu) return | 609 | if (!menu) return |
610 | |||
611 | /** Geocoding by Nominatim */ | 611 | /** Geocoding by Nominatim */ |
612 | // TODO Add more params like limit: | 612 | // TODO Add more params like limit: |
613 | // https://nominatim.org/release-docs/latest/api/Search/ | 613 | // https://nominatim.org/release-docs/latest/api/Search/ |
@@ -623,36 +623,71 @@ export const addLinkbyGeocoding = (range) => { | |||
623 | } | 623 | } |
624 | 624 | ||
625 | // Add items for each results | 625 | // Add items for each results |
626 | const items = places.map(geocodingResult((a) => { | 626 | const bbox = places.map(p => p.boundingbox).reduce((acc, cur) => [ |
627 | Math.min(acc[0], cur[0]), | ||
628 | Math.max(acc[1], cur[1]), | ||
629 | Math.min(acc[2], cur[2]), | ||
630 | Math.max(acc[3], cur[3]), | ||
631 | ]) | ||
632 | const bounds = [[bbox[2], bbox[0]], [bbox[3], bbox[1]]] | ||
633 | const items = places.map(geocodingResult(bounds, (a) => { | ||
627 | a.className = 'not-geolink from-geocoding' | 634 | a.className = 'not-geolink from-geocoding' |
628 | a.textContent = query | 635 | a.textContent = query |
629 | range.deleteContents() | 636 | range.deleteContents() |
630 | range.insertNode(a) | 637 | range.insertNode(a) |
631 | })) | 638 | })) |
632 | menu.replaceChildren(...items) | 639 | menu.replaceChildren(...items) |
640 | shiftByWindow(menu) | ||
633 | }, | 641 | }, |
634 | }) | 642 | }) |
635 | } | 643 | } |
636 | 644 | ||
637 | export const geocodingResult = (callback) => (result) => { | 645 | /** |
646 | * geocodingResult. | ||
647 | * | ||
648 | * @param {Array<Number[]>} bounds - boundingbox in format: [minLon, minLat, maxLon, maxLat] | ||
649 | * @param {Function} callback | ||
650 | */ | ||
651 | export const geocodingResult = (bounds, callback) => (result) => { | ||
638 | const item = Item({ | 652 | const item = Item({ |
639 | text: result.display_name, | 653 | text: result.display_name, |
640 | onclick: () => { | 654 | onclick: (e) => { |
655 | e.target.classList.add('clicked') | ||
656 | |||
641 | const a = document.createElement('a') | 657 | const a = document.createElement('a') |
642 | a.href = `geo:${result.lat},${result.lon}?name=${result.name}&osm=${result.osm_type}/${result.osm_id}` | 658 | a.href = `geo:${result.lat},${result.lon}` + |
659 | `?name=${result.name}` + | ||
660 | `&osm=${result.osm_type}/${result.osm_id}` | ||
643 | a.title = result.display_name | 661 | a.title = result.display_name |
644 | callback(a) | 662 | callback(a) |
645 | }, | 663 | }, |
646 | }) | 664 | }) |
665 | |||
666 | const xy = [result.lon, result.lat] | ||
667 | |||
668 | const markers = getMarkersFromMaps(xy, { | ||
669 | type: 'circle', | ||
670 | title: result.display_name, | ||
671 | }) | ||
672 | const bbox = result.boundingbox | ||
673 | const resultBounds = [[bbox[2], bbox[0]], [bbox[3], bbox[1]]] | ||
674 | |||
647 | item.onmouseover = () => { | 675 | item.onmouseover = () => { |
648 | const markers = getMarkersFromMaps( | 676 | markers.forEach(async marker => { |
649 | [result.lon, result.lat], | 677 | const renderer = marker.closest('.mapclay')?.renderer |
650 | { type: 'circle', title: result.display_name }, | 678 | await renderer.updateCamera({ bounds, duration: 1000, animation: true, padding: 20 }) |
651 | ) | 679 | await renderer.updateCamera({ center: xy, duration: 600, animation: true }) |
652 | markers.forEach(updateMapCameraByMarker([result.lon, result.lat])) | 680 | await renderer.updateCamera({ bounds: resultBounds, duration: 1500, animation: true, padding: 20 }) |
653 | item.onmouseout = () => { | 681 | }) |
654 | markers.forEach(m => m.remove()) | ||
655 | } | ||
656 | } | 682 | } |
683 | |||
684 | setTimeout(() => { | ||
685 | onRemove(item.closest('.menu'), () => { | ||
686 | if (item.classList.contains('clicked')) return | ||
687 | markers.forEach(marker => marker.remove()) | ||
688 | }), | ||
689 | 100 | ||
690 | }) | ||
691 | |||
657 | return item | 692 | return item |
658 | } | 693 | } |
diff --git a/src/utils.mjs b/src/utils.mjs index c0da23a..4cce323 100644 --- a/src/utils.mjs +++ b/src/utils.mjs | |||
@@ -113,6 +113,7 @@ export function debounce (func, delay = 1000) { | |||
113 | * @param {HTMLElement} element | 113 | * @param {HTMLElement} element |
114 | */ | 114 | */ |
115 | export const shiftByWindow = element => { | 115 | export const shiftByWindow = element => { |
116 | delete element.style.transform | ||
116 | const rect = element.getBoundingClientRect() | 117 | const rect = element.getBoundingClientRect() |
117 | const offsetX = window.innerWidth - rect.left - rect.width | 118 | const offsetX = window.innerWidth - rect.left - rect.width |
118 | const offsetY = window.innerHeight - rect.top - rect.height | 119 | const offsetY = window.innerHeight - rect.top - rect.height |