aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/MapBox/MapBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/MapBox/MapBox.tsx')
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx178
1 files changed, 71 insertions, 107 deletions
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 792cb6b46..a563b7c1b 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,8 +1,8 @@
-import { IconLookup, faCircleXmark, faGear, faPause, faPlay, faRotate } from '@fortawesome/free-solid-svg-icons';
+import { IconButton, Size, Type } from '@dash/components';
+import { faCircleXmark, faGear, faPause, faPlay, faRotate } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Checkbox, FormControlLabel, TextField } from '@mui/material';
import * as turf from '@turf/turf';
-import { IconButton, Size, Type } from '@dash/components';
import * as d3 from 'd3';
import { Feature, FeatureCollection, GeoJsonProperties, Geometry, LineString } from 'geojson';
import { LngLatBoundsLike, LngLatLike, MapLayerMouseEvent } from 'mapbox-gl';
@@ -10,11 +10,14 @@ import { IReactionDisposer, ObservableMap, action, autorun, computed, makeObserv
import { observer } from 'mobx-react';
import * as React from 'react';
import { CirclePicker, ColorResult } from 'react-color';
-import { Layer, MapProvider, MapRef, Map as MapboxMap, Marker, Source, ViewState, ViewStateChangeEvent } from 'react-map-gl';
+import { Layer, MapProvider, MapRef, Map as MapboxMap, Marker, Source, ViewState, ViewStateChangeEvent } from 'react-map-gl/mapbox';
import { ClientUtils, setupMoveUpEvents } from '../../../../ClientUtils';
import { emptyFunction } from '../../../../Utils';
-import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field, LinkedTo, Opt, StrListCast } from '../../../../fields/Doc';
+import { List } from '../../../../fields/List';
+import { RichTextField } from '../../../../fields/RichTextField';
import { DocCast, NumCast, StrCast, toList } from '../../../../fields/Types';
+import { TraceMobx } from '../../../../fields/util';
import { DocUtils } from '../../../documents/DocUtils';
import { DocumentType } from '../../../documents/DocumentTypes';
import { Docs } from '../../../documents/Documents';
@@ -34,7 +37,6 @@ import { MapAnchorMenu } from './MapAnchorMenu';
import './MapBox.scss';
import { MapboxApiUtility, TransportationType } from './MapboxApiUtility';
import { MarkerIcons } from './MarkerIcons';
-import { RichTextField } from '../../../../fields/RichTextField';
// import { GeocoderControl } from './GeocoderControl';
// amongus
@@ -76,7 +78,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
makeObservable(this);
}
- @observable _featuresFromGeocodeResults: { place_name: string; center: LngLatLike | undefined }[] = [];
+ @observable _featuresFromGeocodeResults: { place_name: string; center: LngLatLike | undefined; properties?: { wikiData: string } }[] = [];
@observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
@observable _selectedPinOrRoute: Doc | undefined = undefined; // The pin that is selected
@observable _mapReady = false;
@@ -123,7 +125,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
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>;
@@ -183,7 +184,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
});
return feature;
}
- console.log('ERROR');
return {
type: 'Feature',
properties: {},
@@ -199,7 +199,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@computed get allRoutesGeoJson(): FeatureCollection {
const features: Feature<Geometry, GeoJsonProperties>[] = this.allRoutes.map((routeDoc: Doc) => {
- console.log('Route coords: ', routeDoc.routeCoordinates);
const geometry: LineString = {
type: 'LineString',
coordinates: JSON.parse(StrCast(routeDoc.routeCoordinates)),
@@ -215,7 +214,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return {
type: 'FeatureCollection',
- features: features,
+ features,
};
}
@@ -241,7 +240,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
toList(docs).forEach(doc => {
let existingPin = this.allPushpins.find(pin => pin.latitude === doc.latitude && pin.longitude === doc.longitude) ?? this._selectedPinOrRoute;
if (doc.latitude !== undefined && doc.longitude !== undefined && !existingPin) {
- existingPin = this.createPushpin(NumCast(doc.latitude), NumCast(doc.longitude), StrCast(doc.map));
+ existingPin = this.createPushpin({ lng: NumCast(doc.longitude), lat: NumCast(doc.latitude) }, StrCast(doc.map));
}
if (existingPin) {
setTimeout(() => {
@@ -477,7 +476,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@action
deleteSelectedPinOrRoute = undoable(() => {
- console.log('deleting');
if (this._selectedPinOrRoute) {
// Removes filter
Doc.setDocFilter(this.Document, 'latitude', NumCast(this._selectedPinOrRoute.latitude), 'remove');
@@ -542,17 +540,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
* Creates Pushpin doc and adds it to the list of annotations
*/
@action
- createPushpin = undoable((center: LngLatLike, location?: string, wikiData?: string) => {
- const lat = 'lat' in center ? center.lat : center[0];
- const lon = 'lng' in center ? center.lng : 'lon' in center ? center.lon : center[1];
+ createPushpin = (center: LngLatLike, location?: string, wikiData?: string) => {
+ const [lng, lat] = center instanceof Array ? center : ['lng' in center ? center.lng : center.lon, center.lat];
// Stores the pushpin as a MapMarkerDocument
const pushpin = Docs.Create.PushpinDocument(
lat,
- lon,
+ lng,
false,
[],
{
- title: location ?? `lat=${lat},lng=${lon}`,
+ title: location ?? `lat=${lat},lng=${lng}`,
map: location,
description: '',
wikiData: wikiData,
@@ -563,11 +560,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
// ,'pushpinIDamongus'+ this.incrementer++
);
this.addDocument(pushpin, this.annotationKey);
- console.log(pushpin);
return pushpin;
// mapMarker.infoWindowOpen = true;
- }, 'createpin');
+ };
@action
createMapRoute = undoable((coordinates: Position[], originName: string, destination: { place_name: string; center: number[] }, createPinForDestination: boolean) => {
@@ -575,7 +571,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const mapRoute = Docs.Create.MapRouteDocument(false, [], { title: `${originName} --> ${destination.place_name}`, routeCoordinates: JSON.stringify(coordinates) });
this.addDocument(mapRoute, this.annotationKey);
if (createPinForDestination) {
- this.createPushpin(destination.center[1], destination.center[0], destination.place_name);
+ this.createPushpin({ lng: destination.center[0], lat: destination.center[1] }, destination.place_name);
}
this._temporaryRouteSource = {
type: 'FeatureCollection',
@@ -598,18 +594,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
@action
- addMarkerForFeature = (feature: { place_name: string; center: LngLatLike | undefined; properties?: { wikiData: unknown } }) => {
- const location = feature.place_name;
+ addMarkerForFeature = (feature: { place_name: string; center: LngLatLike | undefined; properties?: { wikiData: string } }) => {
if (feature.center) {
- const wikiData = feature.properties?.wikiData;
-
- this.createPushpin(feature.center, location, wikiData);
-
- if (this._mapRef.current) {
- this._mapRef.current.flyTo({
- center: feature.center,
- });
- }
+ this.createPushpin(feature.center, feature.place_name, feature.properties?.wikiData);
+ this._mapRef.current?.flyTo({
+ center: feature.center,
+ });
this._featuresFromGeocodeResults = [];
} else {
// TODO: handle error
@@ -632,7 +622,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
// try {
// const url = MAPBOX_FORWARD_GEOCODE_BASE_URL + encodeURI(searchText) +'.json' +`?access_token=${MAPBOX_ACCESS_TOKEN}`;
// const response = await fetch(url);
- // const data = await response.json();
+ // const data = await response.jchildDocson();
// runInAction(() => {
// this.featuresFromGeocodeResults = data.features;
// })
@@ -653,7 +643,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
layers: ['map-routes-layer'],
});
- console.error(features);
+ this.Document._childFilters = new List<string>(StrListCast(this.Document._childFilters).filter(filter => !filter.includes(LinkedTo)));
if (features && features.length > 0 && features[0].properties && features[0].geometry) {
const { routeTitle } = features[0].properties;
const routeDoc: Doc | undefined = this.allRoutes.find(rtDoc => rtDoc.title === routeTitle);
@@ -668,9 +658,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
MapAnchorMenu.Instance.OnClick = this.createNoteAnnotation;
MapAnchorMenu.Instance.StartDrag = this.startAnchorDrag;
-
MapAnchorMenu.Instance.Reset();
-
MapAnchorMenu.Instance.setRouteDoc(routeDoc);
// TODO: Subject to change
@@ -833,10 +821,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@computed
get preAnimationViewState() {
- if (!this._isAnimating) {
- return this.mapboxMapViewState;
- }
- return undefined;
+ return !this._isAnimating ? this.mapboxMapViewState : undefined;
}
@action
@@ -846,24 +831,15 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@action
updateAnimationSpeed = () => {
- let newAnimationSpeed: AnimationSpeed;
+ this._animationSpeed = (() => {
switch (this._animationSpeed) {
- case AnimationSpeed.SLOW:
- newAnimationSpeed = AnimationSpeed.MEDIUM;
- break;
- case AnimationSpeed.MEDIUM:
- newAnimationSpeed = AnimationSpeed.FAST;
- break;
- case AnimationSpeed.FAST:
- newAnimationSpeed = AnimationSpeed.SLOW;
- break;
- default:
- newAnimationSpeed = AnimationSpeed.MEDIUM;
- break;
- }
- this._animationSpeed = newAnimationSpeed;
+ case AnimationSpeed.SLOW: return AnimationSpeed.MEDIUM;
+ case AnimationSpeed.MEDIUM: return AnimationSpeed.FAST;
+ case AnimationSpeed.FAST: return AnimationSpeed.SLOW;
+ default: return AnimationSpeed.MEDIUM;
+ }})(); // prettier-ignore
if (this._animationUtility) {
- this._animationUtility.updateAnimationSpeed(newAnimationSpeed);
+ this._animationUtility.updateAnimationSpeed(this._animationSpeed);
}
};
@computed get animationSpeedTooltipText(): string {
@@ -890,19 +866,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this._animationUtility?.updateIsStreetViewAnimation(newVal);
};
- getFeatureFromRouteDoc = (routeDoc: Doc): Feature<Geometry, GeoJsonProperties> => {
- const geometry: LineString = {
+ getFeatureFromRouteDoc = (routeDoc: Doc): Feature<Geometry, GeoJsonProperties> => ({
+ type: 'Feature',
+ properties: {
+ routeTitle: routeDoc.title,
+ },
+ geometry: {
type: 'LineString',
coordinates: JSON.parse(StrCast(routeDoc.routeCoordinates)),
- };
- return {
- type: 'Feature',
- properties: {
- routeTitle: routeDoc.title,
- },
- geometry: geometry,
- };
- };
+ },
+ });
@action
playAnimation = (status: AnimationStatus) => {
@@ -936,7 +909,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const updateAnimationPhase = (newAnimationPhase: number) => this.setAnimationPhase(newAnimationPhase);
if (status !== AnimationStatus.RESUME) {
- const result = await animationUtil.flyInAndRotate({
+ await animationUtil.flyInAndRotate({
map: this._mapRef.current!,
// targetLngLat,
// duration 3000
@@ -948,9 +921,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
// endPitch: this.isStreetViewAnimation ? 80 : 50,
updateFrameId,
});
-
- console.log('Bearing: ', result.bearing);
- console.log('Altitude: ', result.altitude);
}
runInAction(() => {
@@ -1028,7 +998,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this.playAnimation(AnimationStatus.START); // Play from the beginning
}
}}
- icon={this._isAnimating && this._finishedFlyTo ? <FontAwesomeIcon icon={faPause as IconLookup} /> : <FontAwesomeIcon icon={faPlay as IconLookup} />}
+ icon={<FontAwesomeIcon icon={this._isAnimating && this._finishedFlyTo ? faPause : faPlay} />}
color="black"
size={Size.MEDIUM}
/>
@@ -1039,12 +1009,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this.stopAnimation(false);
this.playAnimation(AnimationStatus.START);
}}
- icon={<FontAwesomeIcon icon={faRotate as IconLookup} />}
+ icon={<FontAwesomeIcon icon={faRotate} />}
color="black"
size={Size.MEDIUM}
/>
)}
- <IconButton style={{ marginRight: '10px' }} tooltip="Stop and close animation" onPointerDown={() => this.stopAnimation(true)} icon={<FontAwesomeIcon icon={faCircleXmark as IconLookup} />} color="black" size={Size.MEDIUM} />
+ <IconButton style={{ marginRight: '10px' }} tooltip="Stop and close animation" onPointerDown={() => this.stopAnimation(true)} icon={<FontAwesomeIcon icon={faCircleXmark} />} color="black" size={Size.MEDIUM} />
<div className="animation-suboptions">
<div>|</div>
<FormControlLabel className="first-person-label" label="1st person animation:" labelPlacement="start" control={<Checkbox color="success" checked={this._isStreetViewAnimation} onChange={this.toggleIsStreetViewAnimation} />} />
@@ -1085,7 +1055,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
onBearingChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const bearing = parseInt(e.target.value);
if (!isNaN(bearing) && this._mapRef.current) {
- console.log('bearing change');
const fixedBearing = Math.max(0, Math.min(360, bearing));
this._mapRef.current.setBearing(fixedBearing);
this.dataDoc.map_bearing = fixedBearing;
@@ -1096,7 +1065,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
onPitchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const pitch = parseInt(e.target.value);
if (!isNaN(pitch) && this._mapRef.current) {
- console.log('pitch change');
const fixedPitch = Math.max(0, Math.min(85, pitch));
this._mapRef.current.setPitch(fixedPitch);
this.dataDoc.map_pitch = fixedPitch;
@@ -1141,16 +1109,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this._showTerrain = !this._showTerrain;
};
- getMarkerIcon = (pinDoc: Doc): JSX.Element | null => {
- const markerType = StrCast(pinDoc.markerType);
- const markerColor = StrCast(pinDoc.markerColor);
-
- return MarkerIcons.getFontAwesomeIcon(markerType, '2x', markerColor) ?? null;
- };
+ getMarkerIcon = (pinDoc: Doc) => MarkerIcons.getFontAwesomeIcon(StrCast(pinDoc.markerType), '2x', StrCast(pinDoc.markerColor)) ?? null;
render() {
- const scale = this._props.NativeDimScaling?.() || 1;
- const parscale = scale === 1 ? 1 : (this.ScreenToLocalBoxXf().Scale ?? 1);
+ TraceMobx();
+ const scale = (this._props.NativeDimScaling?.() || 1) + 0.001; // bcz: weird, but without this hack, MapBox doesn't locate map correctly
+ const parscale = this.ScreenToLocalBoxXf().Scale;
return (
<div className="mapBox" ref={this._ref}>
@@ -1158,13 +1122,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
className="mapBox-wrapper"
onWheel={e => e.stopPropagation()}
onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()}
- style={{ transformOrigin: 'top left', transform: `scale(${scale})`, width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
+ style={{ transform: `scale(${scale})`, width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
{!this._routeToAnimate && (
- <div className="mapBox-searchbar" style={{ width: `${100 / scale}%`, zIndex: 1, position: 'relative', background: 'lightGray' }}>
+ <div className="mapBox-searchbar" style={{ width: `${100 / scale}%` }}>
<TextField fullWidth placeholder="Enter a location" onKeyDown={this.searchbarKeyDown} onChange={e => this.handleSearchChange(e.target.value)} />
- <IconButton icon={<FontAwesomeIcon icon={faGear as IconLookup} size="1x" />} type={Type.TERT} onClick={() => this.toggleSettings()} />
+ <IconButton icon={<FontAwesomeIcon icon={faGear} size="1x" />} type={Type.TERT} onClick={this.toggleSettings} />
<div style={{ opacity: 0 }}>
- <IconButton icon={<FontAwesomeIcon icon={faGear as IconLookup} size="1x" />} type={Type.TERT} onClick={() => this.toggleSettings()} />
+ <IconButton icon={<FontAwesomeIcon icon={faGear} size="1x" />} type={Type.TERT} onClick={this.toggleSettings} />
</div>
</div>
)}
@@ -1188,15 +1152,15 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
</div>
<div className="mapbox-bearing-selection">
<div>Bearing: </div>
- <input value={NumCast(this.mapboxMapViewState.bearing).toFixed(0)} type="number" onChange={this.onBearingChange} />
+ <input value={this.mapboxMapViewState.bearing.toFixed(0)} type="number" onChange={this.onBearingChange} />
</div>
<div className="mapbox-pitch-selection">
<div>Pitch: </div>
- <input value={NumCast(this.mapboxMapViewState.pitch).toFixed(0)} type="number" onChange={this.onPitchChange} />
+ <input value={this.mapboxMapViewState.pitch.toFixed(0)} type="number" onChange={this.onPitchChange} />
</div>
<div className="mapbox-pitch-selection">
<div>Zoom: </div>
- <input value={NumCast(this.mapboxMapViewState.zoom).toFixed(0)} type="number" onChange={this.onZoomChange} />
+ <input value={this.mapboxMapViewState.zoom.toFixed(0)} type="number" onChange={this.onZoomChange} />
</div>
<div className="mapbox-terrain-selection">
<div>Show terrain: </div>
@@ -1230,17 +1194,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
)}
<MapProvider>
<MapboxMap
+ key={'' + this.Document.x + this.Document.y} // force map to rerender after dragging, otherwise it will display the wrong location until it gets re-rendered
ref={this._mapRef}
mapboxAccessToken={MAPBOX_ACCESS_TOKEN}
- viewState={this._isAnimating || this._routeToAnimate ? undefined : { ...this.mapboxMapViewState, width: NumCast(this.layoutDoc._width), height: NumCast(this.layoutDoc._height) }}
+ viewState={this._isAnimating || this._routeToAnimate ? undefined : { ...this.mapboxMapViewState, width: this._props.PanelWidth(), height: this._props.PanelHeight() }}
mapStyle={this.dataDoc.map_style ? StrCast(this.dataDoc.map_style) : 'mapbox://styles/mapbox/streets-v11'}
style={{
position: 'absolute',
top: 0,
left: 0,
zIndex: '0',
- width: NumCast(this.layoutDoc._width) * parscale,
- height: NumCast(this.layoutDoc._height) * parscale,
+ width: this._props.PanelWidth() * parscale,
+ height: this._props.PanelHeight() * parscale,
}}
initialViewState={this._isAnimating ? undefined : this.mapboxMapViewState}
onZoom={this.onMapZoom}
@@ -1315,19 +1280,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
/>
</>
)}
-
- {!this._isAnimating &&
- this._animationPhase === 0 &&
- this.allPushpins // .filter(anno => !anno.layout_unrendered)
- .map((pushpin, idx) => (
- <Marker key={idx} longitude={NumCast(pushpin.longitude)} latitude={NumCast(pushpin.latitude)} anchor="bottom" onClick={e => this.handleMarkerClick(e.originalEvent.clientX, e.originalEvent.clientY, pushpin)}>
- {this.getMarkerIcon(pushpin)}
- </Marker>
- ))}
-
- {/* {this.mapMarkers.length > 0 && this.mapMarkers.map((marker, idx) => (
- <Marker key={idx} longitude={marker.longitude} latitude={marker.latitude}/>
- ))} */}
+ {this._isAnimating || this._animationPhase
+ ? null
+ : this.allPushpins.map(p => (
+ <Marker
+ key={'' + p.longitude + p.latitude}
+ longitude={NumCast(p.longitude)}
+ latitude={NumCast(p.latitude)}
+ anchor="bottom"
+ onClick={e => this.handleMarkerClick(e.originalEvent.clientX, e.originalEvent.clientY, p)}>
+ {this.getMarkerIcon(p)}
+ </Marker>
+ ))}
</MapboxMap>
</MapProvider>
</div>
@@ -1336,7 +1300,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
ref={this._sidebarRef}
{...this._props}
fieldKey={this.fieldKey}
- Document={this.Document}
+ Doc={this.Document}
layoutDoc={this.layoutDoc}
dataDoc={this.dataDoc}
usePanelWidth