1 parent
6df463391e
commit
07b6b7b243
5 files changed
+180
-166
No files matched your search
@@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import { Map, Marker, LngLatBounds, LngLat, Popup, ScaleControl, NavigationControl, setWorkerUrl } from 'maplibre-gl';
|
||||
import { Map, Marker, LngLatBounds, LngLat, MercatorCoordinate, Popup, ScaleControl, NavigationControl, setWorkerUrl } from 'maplibre-gl';
|
||||
import maplibreWorkerUrl from 'maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url';
|
||||
import 'maplibre-gl/dist/maplibre-gl.css';
|
||||
|
||||
@@ -450,19 +450,69 @@ export default {
|
||||
}
|
||||
|
||||
this.map.fitBounds(oBounds, {
|
||||
padding: BASE_MAP_PADDING,
|
||||
padding: this.map.getPadding(),
|
||||
absolutePadding: true,
|
||||
animate: false,
|
||||
maxZoom: this.maxZoom,
|
||||
pitch: this.initialPitch,
|
||||
bearing: 0
|
||||
});
|
||||
|
||||
this.fixPitchedCameraCenter(aoTrackCoordinates);
|
||||
this.fixPitchedCamera(aoTrackCoordinates);
|
||||
}
|
||||
},
|
||||
fixPitchedCameraCenter(aoTrackCoordinates) {
|
||||
fixPitchedCamera(aoTrackCoordinates) {
|
||||
//fitBounds ignores pitch (still the case in maplibre 6.12): re-center and re-zoom on the projected track.
|
||||
//Perspective keeps convex shapes convex, so the track screen bounds are always reached on its convex hull
|
||||
const aoHull = this.getConvexHull(aoTrackCoordinates);
|
||||
|
||||
//Re-centering changes perspective distortion: a couple of passes on the hull converge
|
||||
for(let iPass = 0; iPass < 3; iPass++) {
|
||||
const oScreenBounds = this.getScreenBounds(aoHull);
|
||||
const oPadding = this.map.getPadding();
|
||||
const oContainer = this.map.getContainer();
|
||||
|
||||
//Zooming scales the screen around the map center, located at the center of the padded area
|
||||
const fScale = Math.min(
|
||||
(oContainer.clientWidth - oPadding.left - oPadding.right) / (oScreenBounds.maxX - oScreenBounds.minX),
|
||||
(oContainer.clientHeight - oPadding.top - oPadding.bottom) / (oScreenBounds.maxY - oScreenBounds.minY)
|
||||
);
|
||||
|
||||
this.map.jumpTo({
|
||||
center: this.map.unproject([
|
||||
(oScreenBounds.minX + oScreenBounds.maxX) / 2,
|
||||
(oScreenBounds.minY + oScreenBounds.maxY) / 2
|
||||
]),
|
||||
zoom: (isFinite(fScale) && fScale > 0)?Math.min(this.map.getZoom() + Math.log2(fScale), this.maxZoom):this.map.getZoom()
|
||||
});
|
||||
|
||||
//Close enough
|
||||
if(Math.abs(fScale - 1) < 0.01) break;
|
||||
}
|
||||
},
|
||||
getConvexHull(aoCoordinates) {
|
||||
//Andrew's monotone chain, on mercator coordinates (projected plane)
|
||||
const aoPoints = aoCoordinates
|
||||
.map(coord => ({coord, merc: MercatorCoordinate.fromLngLat(coord)}))
|
||||
.sort((a, b) => (a.merc.x - b.merc.x) || (a.merc.y - b.merc.y));
|
||||
if(aoPoints.length < 3) return aoCoordinates;
|
||||
|
||||
const cross = (o, a, b) => (a.merc.x - o.merc.x) * (b.merc.y - o.merc.y) - (a.merc.y - o.merc.y) * (b.merc.x - o.merc.x);
|
||||
const buildChain = (aoSortedPoints) => {
|
||||
const aoChain = [];
|
||||
for(const oPoint of aoSortedPoints) {
|
||||
while(aoChain.length >= 2 && cross(aoChain[aoChain.length - 2], aoChain[aoChain.length - 1], oPoint) <= 0) aoChain.pop();
|
||||
aoChain.push(oPoint);
|
||||
}
|
||||
aoChain.pop();
|
||||
return aoChain;
|
||||
};
|
||||
|
||||
return [...buildChain(aoPoints), ...buildChain([...aoPoints].reverse())].map(oPoint => oPoint.coord);
|
||||
},
|
||||
getScreenBounds(aoCoordinates) {
|
||||
//Project min/max coords (lat, lng) onto map rectangle corner points (x, y)
|
||||
const oScreenBounds = aoTrackCoordinates.reduce((oBounds, coord) => {
|
||||
return aoCoordinates.reduce((oBounds, coord) => {
|
||||
const oPoint = this.map.project(coord);
|
||||
return {
|
||||
minX: Math.min(oBounds.minX, oPoint.x),
|
||||
@@ -476,20 +526,6 @@ export default {
|
||||
maxX: -Infinity,
|
||||
maxY: -Infinity
|
||||
});
|
||||
|
||||
//Current Rectangle center
|
||||
const oTrackCenter = {
|
||||
x: (oScreenBounds.minX + oScreenBounds.maxX) / 2,
|
||||
y: (oScreenBounds.minY + oScreenBounds.maxY) / 2
|
||||
};
|
||||
|
||||
//Convert back center point (x, y) to coords and Move map to the track center
|
||||
this.map.jumpTo({
|
||||
center: this.map.unproject([
|
||||
oTrackCenter.x,
|
||||
oTrackCenter.y
|
||||
])
|
||||
});
|
||||
},
|
||||
panTo(oLngLat, iZoom, padding, iAnimDuration=500) {
|
||||
return new Promise((resolve) => {
|
||||
|
||||
@@ -66,15 +66,15 @@ export default {
|
||||
</div>
|
||||
<div v-if="options.type=='track'">
|
||||
<div v-if="options.leg" class="section destination">
|
||||
<appIcon :title="lang.get('stats.from')" icon="start" width="fixed" size="lg" :text="options.leg.from" />
|
||||
<appIcon :title="lang.get('stats.to')" icon="start" transform="rotate-180" width="fixed" size="lg" :text="options.leg.to" />
|
||||
<appIcon :title="lang.get('stats.from')" icon="distance" transform="rotate-315" width="fixed" size="lg" :text="options.leg.from" />
|
||||
<appIcon :title="lang.get('stats.to')" icon="distance" transform="rotate-45" width="fixed" size="lg" :text="options.leg.to" />
|
||||
</div>
|
||||
<div v-if="options.leg" class="separator"></div>
|
||||
<div v-if="options.subtype!='hitchhiking'" class="section track-stats">
|
||||
<appIcon :title="lang.get('stats.distance')" icon="distance" width="fixed" size="lg" :text="options.distance+'km'" />
|
||||
<appIcon :title="lang.get('stats.duration')" icon="time" width="fixed" size="lg" :text="options.duration" />
|
||||
<appIcon :title="lang.get('stats.elevation_gain')" icon="elev-gain" width="fixed" size="lg" :text="options.elev_gain+'m'" />
|
||||
<appIcon :title="lang.get('stats.elevation_loss')" icon="elev-drop" width="fixed" size="lg" :text="options.elev_drop+'m'" />
|
||||
<appIcon :title="lang.get('stats.elevation_gain')" icon="distance" transform="rotate-270" width="fixed" size="lg" :text="options.elev_gain+'m'" />
|
||||
<appIcon :title="lang.get('stats.elevation_loss')" icon="distance" transform="rotate-90" width="fixed" size="lg" :text="options.elev_drop+'m'" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="options.type=='project'" class="section year">{{ activeTimeInterval }}</div>
|
||||
|
||||
@@ -8,11 +8,9 @@ import {
|
||||
faChevronLeft,
|
||||
faChevronRight,
|
||||
faCircleCheck,
|
||||
faCircleDown,
|
||||
faCircleExclamation,
|
||||
faCirclePlay,
|
||||
faCircleRight,
|
||||
faCircleUp,
|
||||
faCircleXmark,
|
||||
faClock,
|
||||
faCloudArrowUp,
|
||||
@@ -100,10 +98,7 @@ const ICONS = {
|
||||
'hitchhiking': faCarSide,
|
||||
layers: faLayerGroup,
|
||||
distance: faCircleRight,
|
||||
'elev-drop': faCircleDown,
|
||||
'elev-gain': faCircleUp,
|
||||
download: faFileArrowDown,
|
||||
start: faCirclePlay,
|
||||
|
||||
/* Settings */
|
||||
account: faUser,
|
||||
|
||||
Reference in new issue
Block a user