aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package-lock.json10
-rw-r--r--package.json2
-rw-r--r--src/client/documents/Documents.ts6
-rw-r--r--src/client/views/nodes/MapBox/AnimationUtility.ts147
-rw-r--r--src/client/views/nodes/MapBox/MapBox.scss23
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx384
6 files changed, 409 insertions, 163 deletions
diff --git a/package-lock.json b/package-lock.json
index 311eb9281..b0748aae6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17010,6 +17010,11 @@
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
},
+ "h264-mp4-encoder": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/h264-mp4-encoder/-/h264-mp4-encoder-1.0.12.tgz",
+ "integrity": "sha512-xih3J+Go0o1RqGjhOt6TwXLWWGqLONRPyS8yoMu/RoS/S8WyEv4HuHp1KBsDDl8srZQ3gw9f95JYkCSjCuZbHQ=="
+ },
"handle-thing": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
@@ -32370,6 +32375,11 @@
"loose-envify": "^1.0.0"
}
},
+ "wasm-feature-detect": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.6.1.tgz",
+ "integrity": "sha512-R1i9ED8UlLu/foILNB1ck9XS63vdtqU/tP1MCugVekETp/ySCrBZRk5I/zI67cI1wlQYeSonNm1PLjDHZDNg6g=="
+ },
"watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
diff --git a/package.json b/package.json
index 9a9e7675d..5d53a6c4e 100644
--- a/package.json
+++ b/package.json
@@ -228,6 +228,7 @@
"googleapis": "^40.0.0",
"googlephotos": "^0.2.5",
"got": "^12.0.1",
+ "h264-mp4-encoder": "^1.0.12",
"howler": "^2.2.3",
"html-to-image": "^0.1.3",
"html-to-text": "^5.1.1",
@@ -341,6 +342,7 @@
"util": "^0.12.4",
"uuid": "^3.4.0",
"valid-url": "^1.0.9",
+ "wasm-feature-detect": "^1.6.1",
"web-request": "^1.0.7",
"webpack-cli": "^4.10.0",
"webpack-dev-middleware": "^5.3.1",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 3db9f4f06..f5f140ae9 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -176,12 +176,16 @@ export class DocumentOptions {
_dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units");
latitude?: NUMt = new NumInfo('latitude coordinate for map views', false);
longitude?: NUMt = new NumInfo('longitude coordinate for map views', false);
- routeCoordinates?: STRt = new StrInfo("stores a route's/direction's coordinates (stringified version)"); // for a route document, this stores the route's coordiantes
+ routeCoordinates?: STRt = new StrInfo("stores a route's/direction's coordinates (stringified version)"); // for a route document, this stores the route's coordinates
markerType?: STRt = new StrInfo('Defines the marker type for a pushpin document');
markerColor?: STRt= new StrInfo('Defines the marker color for a pushpin document');
map?: STRt = new StrInfo('text location of map');
map_type?: STRt = new StrInfo('type of map view', false);
map_zoom?: NUMt = new NumInfo('zoom of a map view', false);
+ map_pitch?: NUMt = new NumInfo('pitch of a map view', false);
+ map_bearing?: NUMt = new NumInfo('bearing of a map view', false);
+ map_style?: STRt = new StrInfo('mapbox style for a map view', false);
+
wikiData?: STRt = new StrInfo('WikiData ID related to map location');
description?: STRt = new StrInfo('A description of the document');
_timecodeToShow?: NUMt = new NumInfo('the time that a document should be displayed (e.g., when an annotation shows up as a video plays)', false);
diff --git a/src/client/views/nodes/MapBox/AnimationUtility.ts b/src/client/views/nodes/MapBox/AnimationUtility.ts
index 256acbf13..11b335a96 100644
--- a/src/client/views/nodes/MapBox/AnimationUtility.ts
+++ b/src/client/views/nodes/MapBox/AnimationUtility.ts
@@ -5,9 +5,8 @@ import * as React from 'react';
import * as d3 from "d3";
import * as turf from '@turf/turf';
import { Position } from "@turf/turf";
-import { Feature, FeatureCollection, GeoJsonProperties, Geometry } from "geojson";
-import { observer } from "mobx-react";
-import { action, computed, observable } from "mobx";
+import { Feature, GeoJsonProperties, Geometry } from "geojson";
+import { action, computed, observable, runInAction } from "mobx";
export enum AnimationStatus {
START = 'start',
@@ -21,23 +20,32 @@ export enum AnimationSpeed {
FAST = '3x',
}
-@observer
export class AnimationUtility {
private SMOOTH_FACTOR = 0.95
private ROUTE_COORDINATES: Position[] = [];
+
+ @observable
private PATH: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties>;
+
+ private PATH_DISTANCE: number;
private FLY_IN_START_PITCH = 40;
private FIRST_LNG_LAT: {lng: number, lat: number};
private START_ALTITUDE = 3_000_000;
+ private MAP_REF: MapRef | null;
@observable private isStreetViewAnimation: boolean;
@observable private animationSpeed: AnimationSpeed;
-
+ @observable
private previousLngLat: {lng: number, lat: number};
+ private previousAltitude: number | null = null;
+ private previousPitch: number | null = null;
+
private currentStreetViewBearing: number = 0;
+ private terrainDisplayed: boolean;
+
@computed get flyInEndBearing() {
return this.isStreetViewAnimation ?
this.calculateBearing(
@@ -51,38 +59,103 @@ export class AnimationUtility {
}
)
: -20;
+ }
+
+ @computed get currentAnimationAltitude(): number {
+ if (!this.isStreetViewAnimation) return 20_000;
+ if (!this.terrainDisplayed) return 50;
+ const coords: mapboxgl.LngLatLike = [this.previousLngLat.lng, this.previousLngLat.lat];
+ // console.log('MAP REF: ', this.MAP_REF)
+ // console.log("current elevation: ", this.MAP_REF?.queryTerrainElevation(coords));
+ let altitude = (this.MAP_REF ? (this.MAP_REF.queryTerrainElevation(coords) ?? 0) : 0);
+ if (altitude === 0){
+ altitude+=50;
}
+ if (this.previousAltitude){
+ return this.lerp(altitude, this.previousAltitude, 0.02);
+ }
+ return altitude;
+
+ }
@computed get flyInStartBearing() {
return Math.max(0, Math.min(this.flyInEndBearing + 20, 360)); // between 0 and 360
}
@computed get flyInEndAltitude() {
- return this.isStreetViewAnimation ? 70 : 10000;
+ // return this.isStreetViewAnimation ? (this.currentAnimationAltitude + 70 ): 10_000;
+ return this.currentAnimationAltitude;
+ }
+
+ @computed get currentPitch(): number {
+ if (!this.isStreetViewAnimation) return 50;
+ if (!this.terrainDisplayed) return 80;
+ else {
+ // const groundElevation = 0;
+ const heightAboveGround = this.currentAnimationAltitude;
+ const horizontalDistance = 500;
+
+ let pitch;
+ if (heightAboveGround >= 0){
+ pitch = (90- Math.atan(heightAboveGround/horizontalDistance) * (180/Math.PI));
+ }
+ else {
+ pitch = 80;
+ }
+
+ console.log(Math.max(50, Math.min(pitch, 85)));
+
+ if (this.previousPitch){
+ return this.lerp(Math.max(50, Math.min(pitch, 85)), this.previousPitch, 0.02);
+ }
+ return Math.max(50, Math.min(pitch, 85));
+ }
}
@computed get flyInEndPitch() {
- return this.isStreetViewAnimation ? 80 : 50;
+ return this.currentPitch;
}
@computed get flyToDuration() {
switch (this.animationSpeed) {
case AnimationSpeed.SLOW:
- return 4000;
+ return 4_000;
case AnimationSpeed.MEDIUM:
- return 2500;
+ return 2_500;
case AnimationSpeed.FAST:
- return 1250;
+ return 1_250;
default:
- return 2500;
+ return 2_500;
}
}
@computed get animationDuration(): number {
- return 20_000;
- // compute path distance for a standard speed
- // get animation speed
- // get isStreetViewAnimation (should be slower if so)
+ let scalingFactor: number;
+ const MIN_DISTANCE = 0;
+ const MAX_DISTANCE = 3_000; // anything greater than 3000 is just set to 1 when normalized
+ const MAX_DURATION = this.isStreetViewAnimation ? 120_000 : 60_000;
+
+ const normalizedDistance = Math.min(1, (this.PATH_DISTANCE - MIN_DISTANCE) / (MAX_DISTANCE - MIN_DISTANCE));
+ const easedDistance = d3.easeExpOut(Math.min(normalizedDistance, 1));
+
+ switch (this.animationSpeed){
+ case AnimationSpeed.SLOW:
+ scalingFactor = 250;
+ break;
+ case AnimationSpeed.MEDIUM:
+ scalingFactor = 150;
+ break;
+ case AnimationSpeed.FAST:
+ scalingFactor = 85;
+ break;
+ default:
+ scalingFactor = 150;
+ break;
+ }
+
+ const duration = Math.min(MAX_DURATION, (easedDistance * MAX_DISTANCE) * (this.isStreetViewAnimation ? scalingFactor*1.12 : scalingFactor));
+
+ return duration;
}
@action
@@ -96,17 +169,29 @@ export class AnimationUtility {
this.isStreetViewAnimation = isStreetViewAnimation;
}
+ @action
+ public setPath = (path: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties>) => {
+ this.PATH = path;
+ }
+
constructor(
firstLngLat: {lng: number, lat: number},
routeCoordinates: Position[],
isStreetViewAnimation: boolean,
- animationSpeed: AnimationSpeed
+ animationSpeed: AnimationSpeed,
+ terrainDisplayed: boolean,
+ mapRef: MapRef | null
) {
this.FIRST_LNG_LAT = firstLngLat;
this.previousLngLat = firstLngLat;
+ this.isStreetViewAnimation = isStreetViewAnimation;
+ this.MAP_REF = mapRef;
+
this.ROUTE_COORDINATES = routeCoordinates;
this.PATH = turf.lineString(routeCoordinates);
+ this.PATH_DISTANCE = turf.lineDistance(this.PATH);
+ this.terrainDisplayed = terrainDisplayed;
const bearing = this.calculateBearing(
{
@@ -119,13 +204,7 @@ export class AnimationUtility {
}
);
this.currentStreetViewBearing = bearing;
- // if (isStreetViewAnimation){
- // this.flyInEndBearing = bearing;
- // }
- this.isStreetViewAnimation = isStreetViewAnimation;
this.animationSpeed = animationSpeed;
- // calculate animation duration based on speed
- // this.animationDuration = animationDuration;
}
public animatePath = async ({
@@ -151,8 +230,6 @@ export class AnimationUtility {
}) => {
return new Promise<void>(async (resolve) => {
- const pathDistance = turf.lineDistance(this.PATH);
- console.log("PATH DISTANCE: ", pathDistance);
let startTime: number | null = null;
const frame = async (currentTime: number) => {
@@ -169,7 +246,7 @@ export class AnimationUtility {
// calculate the distance along the path based on the animationPhase
- const alongPath = turf.along(this.PATH, pathDistance * animationPhase).geometry
+ const alongPath = turf.along(this.PATH, this.PATH_DISTANCE * animationPhase).geometry
.coordinates;
const lngLat = {
@@ -182,7 +259,7 @@ export class AnimationUtility {
bearing = this.lerp(
this.currentStreetViewBearing,
this.calculateBearing(this.previousLngLat, lngLat),
- 0.028 // Adjust the transition speed as needed
+ 0.032
);
this.currentStreetViewBearing = bearing;
// bearing = this.calculateBearing(this.previousLngLat, lngLat); // TODO: Calculate actual bearing
@@ -192,35 +269,39 @@ export class AnimationUtility {
// bearing = startBearing - animationPhase * 200.0;
}
- this.previousLngLat = lngLat;
+ runInAction(() => {
+ this.previousLngLat = lngLat;
+ })
updateAnimationPhase(animationPhase);
// compute corrected camera ground position, so that he leading edge of the path is in view
var correctedPosition = this.computeCameraPosition(
this.isStreetViewAnimation,
- this.flyInEndPitch,
+ this.currentPitch,
bearing,
lngLat,
- this.flyInEndAltitude,
+ this.currentAnimationAltitude,
true // smooth
);
// set the pitch and bearing of the camera
const camera = map.getFreeCameraOptions();
- camera.setPitchBearing(this.flyInEndPitch, bearing);
+ camera.setPitchBearing(this.currentPitch, bearing);
- console.log("Corrected pos: ", correctedPosition);
- console.log("Start altitude: ", this.flyInEndAltitude);
+
// set the position and altitude of the camera
camera.position = MercatorCoordinate.fromLngLat(
correctedPosition,
- this.flyInEndAltitude
+ this.currentAnimationAltitude
);
// apply the new camera options
map.setFreeCameraOptions(camera);
+
+ this.previousAltitude = this.currentAnimationAltitude;
+ this.previousPitch = this.previousPitch;
// repeat!
const innerFrameId = await window.requestAnimationFrame(frame);
diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss
index d3c6bb14e..e25261729 100644
--- a/src/client/views/nodes/MapBox/MapBox.scss
+++ b/src/client/views/nodes/MapBox/MapBox.scss
@@ -109,8 +109,14 @@
.animation-suboptions{
display: flex;
justify-content: flex-start;
+ flex-wrap: wrap;
align-items: center;
gap: 7px;
+ width: 100%;
+
+ .first-person-label{
+ width: '130px' !important;
+ }
label{
margin-bottom: 0;
@@ -120,7 +126,7 @@
margin-right: 5px;
}
- #last-divider{
+ #divider{
margin-left: 10px;
margin-right: 10px;
}
@@ -128,6 +134,21 @@
}
+ }
+
+ .zoom-box {
+ position: absolute;
+ z-index: 900;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ background-color: white;
+ font-size: 1.4em;
+ border-radius: 5px;
+ bottom: 5px;
+ left: 5px;
+ padding: 3px;
}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index cde68a2e6..f4526c490 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -3,7 +3,7 @@ import BingMapsReact from 'bingmaps-react';
// import 'mapbox-gl/dist/mapbox-gl.css';
import { Button, EditableText, IconButton, Size, Type } from 'browndash-components';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, flow, toJS} from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, flow, toJS, autorun} from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc';
@@ -28,6 +28,9 @@ import { FieldView, FieldViewProps } from '../FieldView';
import { FormattedTextBox } from '../formattedText/FormattedTextBox';
import { PinProps, PresBox } from '../trails';
import { MapAnchorMenu } from './MapAnchorMenu';
+import * as HME from "h264-mp4-encoder";
+import {simd} from 'wasm-feature-detect';
+
import {
Map as MapboxMap,
MapRef,
@@ -51,20 +54,21 @@ import debounce from 'debounce';
import './MapBox.scss';
import { NumberLiteralType } from 'typescript';
// import { GeocoderControl } from './GeocoderControl';
-import mapboxgl, { LngLat, LngLatBoundsLike, MapLayerMouseEvent, MercatorCoordinate } from 'mapbox-gl';
+import mapboxgl, { LngLat, LngLatBoundsLike, LngLatLike, MapLayerMouseEvent, MercatorCoordinate } from 'mapbox-gl';
import { Feature, FeatureCollection, GeoJsonProperties, Geometry, LineString, MultiLineString, Position } from 'geojson';
import { MarkerEvent } from 'react-map-gl/dist/esm/types';
import { MapboxApiUtility, TransportationType} from './MapboxApiUtility';
import { Autocomplete, Checkbox, FormControlLabel, TextField } from '@mui/material';
import { List } from '../../../../fields/List';
import { listSpec } from '../../../../fields/Schema';
-import { IconLookup, faCircleXmark, faFileExport, faGear, faPause, faPlay, faRotate } from '@fortawesome/free-solid-svg-icons';
+import { IconLookup, faCircleXmark, faFileExport, faGear, faMinus, faPause, faPlay, faPlus, faRotate } from '@fortawesome/free-solid-svg-icons';
import { MarkerIcons } from './MarkerIcons';
import { SettingsManager } from '../../../util/SettingsManager';
import * as turf from '@turf/turf';
import * as d3 from "d3";
import { AnimationSpeed, AnimationStatus, AnimationUtility } from './AnimationUtility';
import { fastSpeedIcon, mediumSpeedIcon, slowSpeedIcon } from './AnimationSpeedIcons';
+import { CirclePicker, ColorState } from 'react-color';
// amongus
/**
@@ -153,17 +157,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const originalCoordinates: Position[] = JSON.parse(StrCast(this.routeToAnimate.routeCoordinates));
// const index = Math.floor(this.animationPhase * originalCoordinates.length);
const index = this.animationPhase * (originalCoordinates.length - 1); // Calculate the fractional index
+ console.log("Animation phase", this.animationPhase);
const startIndex = Math.floor(index);
const endIndex = Math.ceil(index);
+ let feature: Feature<Geometry, GeoJsonProperties>;
+ let geometry: LineString;
if (startIndex === endIndex) {
// AnimationPhase is at a whole number (no interpolation needed)
const coordinates = [originalCoordinates[startIndex]];
- const geometry: LineString = {
+ geometry = {
type: 'LineString',
coordinates,
};
- return {
+ feature = {
type: 'Feature',
properties: {
'routeTitle': StrCast(this.routeToAnimate.title)
@@ -175,20 +182,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const startCoord = originalCoordinates[startIndex];
const endCoord = originalCoordinates[endIndex];
const fraction = index - startIndex;
-
- // Interpolate the coordinates
- const interpolatedCoord = [
- startCoord[0] + fraction * (endCoord[0] - startCoord[0]),
- startCoord[1] + fraction * (endCoord[1] - startCoord[1]),
- ];
+
+ const interpolator = d3.interpolateArray(startCoord, endCoord);
+
+ const interpolatedCoord = interpolator(fraction);
const coordinates = originalCoordinates.slice(0, startIndex + 1).concat([interpolatedCoord]);
-
- const geometry: LineString = {
+
+ geometry = {
type: 'LineString',
coordinates,
};
- return {
+ feature = {
type: 'Feature',
properties: {
'routeTitle': StrCast(this.routeToAnimate.title)
@@ -196,7 +201,25 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
geometry: geometry,
};
}
+
+ autorun(() => {
+ const animationUtil = this.animationUtility;
+ const concattedCoordinates = geometry.coordinates.concat(originalCoordinates.slice(endIndex));
+ const newFeature: Feature<LineString, turf.Properties> = {
+ type: 'Feature',
+ properties: {},
+ geometry: {
+ type: 'LineString',
+ coordinates: concattedCoordinates
+ }
+ }
+ if (animationUtil){
+ animationUtil.setPath(newFeature)
+ }
+ })
+ return feature;
}
+ console.log("ERROR");
return {
type: 'Feature',
properties: {},
@@ -600,7 +623,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
config_latitude: NumCast((existingPin ?? this.selectedPinOrRoute)?.latitude ?? this.dataDoc.latitude),
config_longitude: NumCast((existingPin ?? this.selectedPinOrRoute)?.longitude ?? this.dataDoc.longitude),
config_map_zoom: NumCast(this.dataDoc.map_zoom),
- config_map_type: StrCast(this.dataDoc.map_type),
+ // config_map_type: StrCast(this.dataDoc.map_type),
config_map: StrCast((existingPin ?? this.selectedPinOrRoute)?.map) || StrCast(this.dataDoc.map),
layout_unrendered: true,
mapPin: existingPin ?? this.selectedPinOrRoute,
@@ -1077,9 +1100,17 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
MapAnchorMenu.Instance.setMenuType('standard');
+ // MapAnchorMenu.Instance.jumpTo(NumCast(pinDoc.longitude), NumCast(pinDoc.latitude)-3, true);
+
MapAnchorMenu.Instance.jumpTo(e.originalEvent.clientX, e.originalEvent.clientY, true);
document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
+
+
+ // this._mapRef.current.flyTo({
+ // center: [NumCast(pinDoc.longitude), NumCast(pinDoc.latitude)-3]
+ // })
+
};
@observable
@@ -1114,9 +1145,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
isAnimating: boolean = false;
@observable
- isPaused: boolean = false;
-
- @observable
routeToAnimate: Doc | undefined = undefined;
@observable
@@ -1138,6 +1166,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this.frameId = frameId;
}
+ @observable
+ animationUtility: AnimationUtility | null = null;
+
+ @action
+ setAnimationUtility = (util: AnimationUtility) => {
+ this.animationUtility = util;
+ }
+
@action
openAnimationPanel = (routeDoc: Doc | undefined) => {
if (routeDoc){
@@ -1148,13 +1184,27 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
@observable
- animationDuration = 40000;
+ mapboxMapViewState: ViewState = {
+ zoom: this.dataDoc.map_zoom ? NumCast(this.dataDoc.map_zoom) : 8,
+ longitude: this.dataDoc.longitude ? NumCast(this.dataDoc.longitude) : -71.4128,
+ latitude: this.dataDoc.latitude ? NumCast(this.dataDoc.latitude) : 41.8240,
+ pitch: this.dataDoc.map_pitch ? NumCast(this.dataDoc.map_pitch) : 0,
+ bearing: this.dataDoc.map_bearing ? NumCast(this.dataDoc.map_bearing) : 0,
+ padding: {
+ top: 0,
+ bottom: 0,
+ left: 0,
+ right: 0
+ },
+ }
- @observable
- animationAltitude = 12800;
+ @computed
+ get preAnimationViewState() {
+ if (!this.isAnimating){
+ return this.mapboxMapViewState;
+ }
+ }
- @observable
- pathDistance = 0;
@observable
isStreetViewAnimation: boolean = false;
@@ -1162,22 +1212,36 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@observable
animationSpeed: AnimationSpeed = AnimationSpeed.MEDIUM;
+
+ @observable
+ animationLineColor: string = '#ffff00';
+
+ @action
+ setAnimationLineColor = (color: ColorState) => {
+ this.animationLineColor = color.hex;
+ }
+
@action
updateAnimationSpeed = () => {
+ let newAnimationSpeed: AnimationSpeed;
switch (this.animationSpeed){
case AnimationSpeed.SLOW:
- this.animationSpeed = AnimationSpeed.MEDIUM;
+ newAnimationSpeed = AnimationSpeed.MEDIUM;
break;
case AnimationSpeed.MEDIUM:
- this.animationSpeed = AnimationSpeed.FAST;
+ newAnimationSpeed = AnimationSpeed.FAST;
break;
case AnimationSpeed.FAST:
- this.animationSpeed = AnimationSpeed.SLOW;
+ newAnimationSpeed = AnimationSpeed.SLOW;
break;
default:
- this.animationSpeed = AnimationSpeed.MEDIUM;
+ newAnimationSpeed = AnimationSpeed.MEDIUM;
break;
}
+ this.animationSpeed = newAnimationSpeed;
+ if (this.animationUtility){
+ this.animationUtility.updateAnimationSpeed(newAnimationSpeed);
+ }
}
@computed get animationSpeedTooltipText(): string {
switch (this.animationSpeed) {
@@ -1206,7 +1270,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@action
toggleIsStreetViewAnimation = () => {
- this.isStreetViewAnimation = !this.isStreetViewAnimation;
+ const newVal = !this.isStreetViewAnimation;
+ this.isStreetViewAnimation = newVal;
+ if (this.animationUtility){
+ this.animationUtility.updateIsStreetViewAnimation(newVal)
+ }
}
@observable
@@ -1249,9 +1317,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return;
}
- if (this.isAnimating){
- return;
- }
this.animationPhase = status === AnimationStatus.RESUME ? this.animationPhase : 0;
this.frameId = AnimationStatus.RESUME ? this.frameId : null;
this.finishedFlyTo = AnimationStatus.RESUME ? this.finishedFlyTo : false;
@@ -1260,23 +1325,26 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this.settingsOpen = false;
this.path = path;
- this.pathDistance = turf.lineDistance(path);
this.isAnimating = true;
+
runInAction(() => {
return new Promise<void>(async (resolve) => {
- let animationUtil;
- try {
const targetLngLat = {
lng: this.selectedRouteCoordinates[0][0],
lat: this.selectedRouteCoordinates[0][1],
};
- animationUtil = new AnimationUtility(
+ const animationUtil = new AnimationUtility(
targetLngLat,
this.selectedRouteCoordinates,
this.isStreetViewAnimation,
- this.animationSpeed
+ this.animationSpeed,
+ this.showTerrain,
+ this._mapRef.current
);
+ runInAction(() => {
+ this.setAnimationUtility(animationUtil);
+ })
const updateFrameId = (newFrameId: number) => {
@@ -1338,14 +1406,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
});
setTimeout(() => {
+ this.isStreetViewAnimation = false;
resolve();
}, 10000);
- } catch (error: any){
- console.log(error);
- console.log('animation util: ', animationUtil);
- }});
+ });
- })
+ })
}
@@ -1361,17 +1427,25 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
@action
- stopAndCloseAnimation = () => {
+ stopAnimation = (close: boolean) => {
if (this.frameId){
window.cancelAnimationFrame(this.frameId);
- this.frameId = null;
- this.finishedFlyTo = false;
- this.isAnimating = false;
- this.animationPhase = 0;
+ }
+ this.animationPhase = 0;
+ this.frameId = null;
+ this.finishedFlyTo = false;
+ this.isAnimating = false;
+ if (close) {
+ this.animationSpeed = AnimationSpeed.MEDIUM;
+ this.isStreetViewAnimation = false;
this.routeToAnimate = undefined;
- // this.selectedRouteCoordinates = [];
+ this.animationUtility = null;
}
- // reset bearing and pitch to original, zoom out
+ if (this.preAnimationViewState){
+ this.mapboxMapViewState = this.preAnimationViewState;
+ }
+
+
}
@action
@@ -1404,7 +1478,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
{this.isAnimating && this.finishedFlyTo &&
<IconButton
tooltip='Restart animation'
- onPointerDown={() => this.playAnimation(AnimationStatus.RESTART)}
+ onPointerDown={() => {
+ this.stopAnimation(false);
+ this.playAnimation(AnimationStatus.START)
+ }}
icon={<FontAwesomeIcon icon={faRotate as IconLookup}/>}
color='black'
size={Size.MEDIUM}
@@ -1412,45 +1489,47 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
<IconButton
+ style={{marginRight: '10px'}}
tooltip='Stop and close animation'
- onPointerDown={this.stopAndCloseAnimation}
+ onPointerDown={() => this.stopAnimation(true)}
icon={<FontAwesomeIcon icon={faCircleXmark as IconLookup}/>}
color='black'
size={Size.MEDIUM}
/>
- <IconButton
- style={{marginRight: '10px'}}
- tooltip='Export to video'
- onPointerDown={this.exportAnimationToVideo}
- icon={<FontAwesomeIcon icon={faFileExport as IconLookup}/>}
- color='black'
- size={Size.MEDIUM}
- />
- {!this.isAnimating &&
- <>
- <div className='animation-suboptions'>
- <div>|</div>
- <FormControlLabel
- label='Street view animation'
- labelPlacement='start'
- control={
- <Checkbox
- color='success'
- checked={this.isStreetViewAnimation}
- onChange={this.toggleIsStreetViewAnimation}
- />
- }
- />
- <div id='last-divider'>|</div>
- <IconButton
- tooltip={this.animationSpeedTooltipText}
- onPointerDown={this.updateAnimationSpeed}
- icon={this.animationSpeedIcon}
- size={Size.MEDIUM}
- />
- </div>
- </>
- }
+ <>
+ <div className='animation-suboptions'>
+ <div>|</div>
+ <FormControlLabel
+ className='first-person-label'
+ style={{width: '130px'}}
+ label='1st person animation:'
+ labelPlacement='start'
+ control={
+ <Checkbox
+ color='success'
+ checked={this.isStreetViewAnimation}
+ onChange={this.toggleIsStreetViewAnimation}
+ />
+ }
+ />
+ <div id='divider'>|</div>
+ <IconButton
+ tooltip={this.animationSpeedTooltipText}
+ onPointerDown={this.updateAnimationSpeed}
+ icon={this.animationSpeedIcon}
+ size={Size.MEDIUM}
+ />
+ <div id='divider'>|</div>
+ <div style={{width: '230px'}}>Select Line Color: </div>
+ <CirclePicker
+ circleSize={12}
+ circleSpacing={5}
+ width='100%'
+ colors={['#ffff00', '#03a9f4', '#ff0000', '#ff5722', '#000000', '#673ab7']}
+ onChange={(color) => this.setAnimationLineColor(color)}
+ />
+ </div>
+ </>
</>
)
}
@@ -1464,22 +1543,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
-
- @observable
- mapboxMapViewState: ViewState = {
- zoom: 9,
- longitude: -71.45,
- latitude: 41.82,
- pitch: 0,
- bearing: 0,
- padding: {
- top: 0,
- bottom: 0,
- left: 0,
- right: 0
- }
- }
-
@observable
settingsOpen: boolean = false;
@@ -1487,7 +1550,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
mapStyle: string = 'mapbox://styles/mapbox/streets-v12'
@observable
- showTerrain: boolean = false;
+ showTerrain: boolean = true;
@action
toggleSettings = () => {
@@ -1499,39 +1562,85 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@action
changeMapStyle = (e: React.ChangeEvent<HTMLSelectElement>) => {
- this.mapStyle = `mapbox://styles/mapbox/${e.target.value}`
+ this.dataDoc.map_style = `mapbox://styles/mapbox/${e.target.value}`;
+ // this.mapStyle = `mapbox://styles/mapbox/${e.target.value}`
}
- @action
- onMapMove = (e: ViewStateChangeEvent) => {
- this.mapboxMapViewState = e.viewState;
+ @action
+ onBearingChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ const bearing = parseInt(e.target.value);
+ if (!isNaN(bearing) && this._mapRef.current){
+ const fixedBearing = Math.max(0, Math.min(360, bearing));
+ this._mapRef.current.setBearing(fixedBearing);
+ this.dataDoc.map_bearing = fixedBearing;
+ this.mapboxMapViewState = {
+ ...this.mapboxMapViewState,
+ bearing: fixedBearing
+ }
+ }
}
- @action
- toggleShowTerrain = () => {
- this.showTerrain = !this.showTerrain;
+ @action
+ onPitchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ const pitch = parseInt(e.target.value);
+ if (!isNaN(pitch) && this._mapRef.current){
+ const fixedPitch = Math.max(0, Math.min(85, pitch));
+ this._mapRef.current.setPitch(fixedPitch);
+ this.dataDoc.map_pitch = fixedPitch;
+ this.mapboxMapViewState = {
+ ...this.mapboxMapViewState,
+ pitch: fixedPitch
+ }
+ }
}
- @action
- onBearingChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- const newVal = parseInt(e.target.value)
- if (!isNaN(newVal) && newVal >= 0){
+ @action
+ onZoomChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ const zoom = parseInt(e.target.value);
+ if (!isNaN(zoom) && this._mapRef.current){
+ const fixedZoom = Math.max(0, Math.min(16, zoom));
+ this._mapRef.current.setZoom(fixedZoom);
+ this.dataDoc.map_zoom = fixedZoom;
this.mapboxMapViewState = {
...this.mapboxMapViewState,
- bearing: parseInt(e.target.value)
+ zoom: fixedZoom
}
}
}
@action
- onPitchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- const newVal = parseInt(e.target.value);
- if (!isNaN(newVal) && newVal >= 0){
+ onStepZoomChange = (increment: boolean) => {
+ if (this._mapRef.current) {
+ let newZoom: number;
+ if (increment) {
+ console.log('inc')
+ newZoom = this.mapboxMapViewState.zoom + 1;
+
+ } else {
+ console.log('dec')
+ newZoom = this.mapboxMapViewState.zoom - 1;
+ }
+ this._mapRef.current.setZoom(newZoom);
+ this.dataDoc.map_zoom = newZoom;
this.mapboxMapViewState = {
...this.mapboxMapViewState,
- pitch: parseInt(e.target.value)
+ zoom: increment ? Math.min(16, newZoom) : Math.max(0, newZoom)
}
}
+
+ }
+
+
+ @action
+ onMapMove = (e: ViewStateChangeEvent) => {
+ this.mapboxMapViewState = e.viewState;
+ this.dataDoc.longitude = e.viewState.longitude;
+ this.dataDoc.latitude = e.viewState.latitude;
+ }
+
+ @action
+ toggleShowTerrain = () => {
+ this.showTerrain = !this.showTerrain;
}
getMarkerIcon = (pinDoc: Doc): JSX.Element | null => {
@@ -1603,7 +1712,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
</div>
<div>
<select onChange={this.changeMapStyle}>
- <option value='streets-v12'>Streets</option>
+ <option value='streets-v11'>Streets</option>
<option value='outdoors-v12'>Outdoors</option>
<option value='light-v11'>Light</option>
<option value='dark-v11'>Dark</option>
@@ -1617,19 +1726,24 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
<div className='mapbox-bearing-selection'>
<div>Bearing: </div>
<input
- value={this.mapboxMapViewState.bearing}
- min={0}
+ value={NumCast(this.mapboxMapViewState.bearing).toFixed(2)}
type='number'
onChange={this.onBearingChange}/>
</div>
<div className='mapbox-pitch-selection'>
<div>Pitch: </div>
<input
- value={this.mapboxMapViewState.pitch}
- min={0}
+ value={NumCast(this.mapboxMapViewState.pitch).toFixed(2)}
type='number'
onChange={this.onPitchChange}/>
- </div>
+ </div>
+ <div className='mapbox-pitch-selection'>
+ <div>Zoom: </div>
+ <input
+ value={NumCast(this.mapboxMapViewState.zoom).toFixed(2)}
+ type='number'
+ onChange={this.onZoomChange}/>
+ </div>
<div className='mapbox-terrain-selection'>
<div>Show terrain: </div>
<input
@@ -1673,16 +1787,30 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
</React.Fragment>
</div>
- )}
+ )}
+ {/* <div className='zoom-box'>
+ <IconButton // increment
+ style={{borderBottom: '1px', borderBottomColor: 'white'}}
+ onPointerDown={() => this.onStepZoomChange(true)}
+ icon={<FontAwesomeIcon icon={faPlus as IconLookup} color='black'/>}
+ size={Size.SMALL}
+ color={SettingsManager.userColor}
+ />
+ <IconButton // decrement
+ onPointerDown={() => this.onStepZoomChange(false)}
+ icon={<FontAwesomeIcon icon={faMinus as IconLookup} color='black'/>}
+ size={Size.SMALL}
+ color={SettingsManager.userColor}
+ />
+ </div> */}
<MapProvider>
<MapboxMap
ref={this._mapRef}
mapboxAccessToken={MAPBOX_ACCESS_TOKEN}
id="mapbox-map"
- mapStyle={this.mapStyle}
- style={{height: '100%', width: '100%'}}
+ mapStyle={this.dataDoc.map_style ? StrCast(this.dataDoc.map_style) : 'mapbox://styles/mapbox/streets-v11'}
+ style={{height: NumCast(this.layoutDoc._height), width: NumCast(this.layoutDoc._width)}}
initialViewState={this.isAnimating ? undefined : this.mapboxMapViewState}
- // {...this.mapboxMapViewState}
onMove={this.onMapMove}
onClick={this.handleMapClick}
onDblClick={this.handleMapDblClick}
@@ -1725,8 +1853,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
type='line'
source='animated-route'
paint={{
- 'line-color': 'yellow',
- 'line-width': 4,
+ 'line-color': this.animationLineColor,
+ 'line-width': 5,
}}
/>
</>