diff options
Diffstat (limited to 'src/client/views/nodes/MapBox')
| -rw-r--r-- | src/client/views/nodes/MapBox/MapBox.scss | 31 | ||||
| -rw-r--r-- | src/client/views/nodes/MapBox/MapBox.tsx | 153 | ||||
| -rw-r--r-- | src/client/views/nodes/MapBox/MapboxApiUtility.ts | 98 |
3 files changed, 133 insertions, 149 deletions
diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss index 434e02b27..0987f8795 100644 --- a/src/client/views/nodes/MapBox/MapBox.scss +++ b/src/client/views/nodes/MapBox/MapBox.scss @@ -4,6 +4,7 @@ height: 100%; overflow: hidden; display: flex; + position: absolute; .mapBox-infoWindow { background-color: white; @@ -27,7 +28,7 @@ // } } - .mapbox-settings-panel{ + .mapbox-settings-panel { z-index: 900; padding: 10px 20px; display: flex; @@ -41,7 +42,7 @@ border-top-left-radius: 5px; border-bottom-left-radius: 5px; - .mapbox-style-select{ + .mapbox-style-select { display: flex; flex-direction: column; align-items: flex-start; @@ -49,14 +50,13 @@ gap: 4px; } - .mapbox-terrain-selection{ + .mapbox-terrain-selection { display: flex; flex-direction: row; align-items: center; justify-content: flex-start; gap: 4px; } - } .mapbox-geocoding-search-results { @@ -75,11 +75,10 @@ .search-result-container { width: 100%; padding: 10px; - &:hover{ + &:hover { background-color: lighten(rgb(187, 187, 187), 10%); } } - } .animation-panel { @@ -88,12 +87,11 @@ flex-direction: column; justify-content: flex-start; align-items: flex-start; - position: absolute; background-color: rgb(187, 187, 187); padding: 10px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; - width: 100%; + position: absolute; #route-to-animate-title { font-size: 1.25em; @@ -106,7 +104,7 @@ align-items: center; gap: 7px; - .animation-suboptions{ + .animation-suboptions { display: flex; justify-content: flex-start; flex-wrap: wrap; @@ -114,25 +112,23 @@ gap: 7px; width: 100%; - .first-person-label{ + .first-person-label { width: '130px' !important; } - label{ + label { margin-bottom: 0; } - - .speed-label{ + + .speed-label { margin-right: 5px; } - #divider{ + #divider { margin-left: 10px; margin-right: 10px; } } - - } } @@ -149,10 +145,8 @@ bottom: 5px; left: 5px; padding: 3px; - } - .mapBox-topbar { display: flex; flex-direction: row; @@ -236,4 +230,3 @@ display: block; } } - diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index c25d10a0d..efe0a3620 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -1,72 +1,45 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -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, autorun, makeObservable } from 'mobx'; +import { IconButton, Size, Type } from 'browndash-components'; +import { IReactionDisposer, ObservableMap, action, autorun, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; +import { Utils, emptyFunction, setupMoveUpEvents } from '../../../../Utils'; import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc'; import { DocCss, Highlight } from '../../../../fields/DocSymbols'; -import { Id } from '../../../../fields/FieldSymbols'; -import { Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; -import { Docs, DocUtils } from '../../../documents/Documents'; +import { DocCast, NumCast, StrCast } from '../../../../fields/Types'; import { DocumentType } from '../../../documents/DocumentTypes'; +import { DocUtils, Docs } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { LinkManager } from '../../../util/LinkManager'; import { SnappingManager } from '../../../util/SnappingManager'; -import { Transform } from '../../../util/Transform'; -import { undoable, UndoManager } from '../../../util/UndoManager'; -import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; +import { UndoManager, undoable } from '../../../util/UndoManager'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent'; -import { Colors } from '../../global/globalEnums'; import { SidebarAnnos } from '../../SidebarAnnos'; +import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; +import { Colors } from '../../global/globalEnums'; import { DocumentView } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { FormattedTextBox } from '../formattedText/FormattedTextBox'; import { PinProps, PresBox } from '../trails'; import { MapAnchorMenu } from './MapAnchorMenu'; -import { - Map as MapboxMap, - MapRef, - Marker, - ControlPosition, - FullscreenControl, - MapProvider, - MarkerProps, - NavigationControl, - ScaleControl, - ViewState, - ViewStateChangeEvent, - useControl, - GeolocateControl, - Popup, - MapEvent, - Source, - Layer, -} from 'react-map-gl'; +import { ControlPosition, Layer, MapProvider, MapRef, Map as MapboxMap, Marker, MarkerProps, Source, ViewState, ViewStateChangeEvent } from 'react-map-gl'; import MapboxGeocoder, { GeocoderOptions } from '@mapbox/mapbox-gl-geocoder!'; import './MapBox.scss'; -import { NumberLiteralType } from 'typescript'; // import { GeocoderControl } from './GeocoderControl'; -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, faMinus, faPause, faPlay, faPlus, faRotate } from '@fortawesome/free-solid-svg-icons'; -import { MarkerIcons } from './MarkerIcons'; -import { SettingsManager } from '../../../util/SettingsManager'; +import { IconLookup, faCircleXmark, faGear, faPause, faPlay, faRotate } from '@fortawesome/free-solid-svg-icons'; +import { Checkbox, FormControlLabel, TextField } from '@mui/material'; import * as turf from '@turf/turf'; import * as d3 from 'd3'; -import { AnimationSpeed, AnimationStatus, AnimationUtility } from './AnimationUtility'; -import { fastSpeedIcon, mediumSpeedIcon, slowSpeedIcon } from './AnimationSpeedIcons'; +import { Feature, FeatureCollection, GeoJsonProperties, Geometry, LineString, Position } from 'geojson'; +import mapboxgl, { LngLat, LngLatBoundsLike, MapLayerMouseEvent } from 'mapbox-gl!'; import { CirclePicker, ColorResult } from 'react-color'; +import { MarkerEvent } from 'react-map-gl/dist/esm/types'; +import { fastSpeedIcon, mediumSpeedIcon, slowSpeedIcon } from './AnimationSpeedIcons'; +import { AnimationSpeed, AnimationStatus, AnimationUtility } from './AnimationUtility'; +import { MapboxApiUtility, TransportationType } from './MapboxApiUtility'; +import { MarkerIcons } from './MarkerIcons'; // amongus /** @@ -891,15 +864,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps }, 'createpin'); @action - createMapRoute = undoable((coordinates: Position[], origin: string, destination: any, createPinForDestination: boolean) => { - const mapRoute = Docs.Create.MapRouteDocument(false, [], { title: `${origin} --> ${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); + createMapRoute = undoable((coordinates: Position[], originName: string, destination: any, createPinForDestination: boolean) => { + if (originName !== destination.place_name) { + 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); + } + return mapRoute; } - return mapRoute; - - // mapMarker.infoWindowOpen = true; + // TODO: Display error that can't create route to same location }, 'createmaproute'); searchbarKeyDown = (e: any) => e.key === 'Enter' && this.bingSearch(); @@ -958,6 +932,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps @action handleMapClick = (e: MapLayerMouseEvent) => { + this.featuresFromGeocodeResults = []; if (this._mapRef.current) { const features = this._mapRef.current.queryRenderedFeatures(e.point, { layers: ['map-routes-layer'], @@ -1146,24 +1121,26 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps if (routeDoc) { MapAnchorMenu.Instance.fadeOut(true); document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true); + this.featuresFromGeocodeResults = []; this.routeToAnimate = routeDoc; } }; - @observable - 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.824, - 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, - }, - }; + @computed get mapboxMapViewState(): ViewState { + return { + zoom: NumCast(this.dataDoc.map_zoom, 8), + longitude: NumCast(this.dataDoc.longitude, -71.4128), + latitude: NumCast(this.dataDoc.latitude, 41.824), + pitch: NumCast(this.dataDoc.map_pitch), + bearing: NumCast(this.dataDoc.map_bearing), + padding: { + top: 0, + bottom: 0, + left: 0, + right: 0, + }, + }; + } @computed get preAnimationViewState() { @@ -1390,9 +1367,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this.routeToAnimate = undefined; this.animationUtility = null; } - if (this.preAnimationViewState) { - this.mapboxMapViewState = this.preAnimationViewState; - } }; @action @@ -1488,10 +1462,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps 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, - }; } }; @@ -1502,10 +1472,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps 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, - }; } }; @@ -1516,10 +1482,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps const fixedZoom = Math.max(0, Math.min(16, zoom)); this._mapRef.current.setZoom(fixedZoom); this.dataDoc.map_zoom = fixedZoom; - this.mapboxMapViewState = { - ...this.mapboxMapViewState, - zoom: fixedZoom, - }; } }; @@ -1529,23 +1491,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps let newZoom: number; if (increment) { console.log('inc'); - newZoom = this.mapboxMapViewState.zoom + 1; + newZoom = Math.min(16, this.mapboxMapViewState.zoom + 1); } else { console.log('dec'); - newZoom = this.mapboxMapViewState.zoom - 1; + newZoom = Math.max(0, this.mapboxMapViewState.zoom - 1); } this._mapRef.current.setZoom(newZoom); this.dataDoc.map_zoom = newZoom; - this.mapboxMapViewState = { - ...this.mapboxMapViewState, - 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; }; @@ -1581,8 +1538,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps }), MapBox._rerenderDelay); return null; } - const scale = this._props.NativeDimScaling?.() || 1; + const parscale = scale === 1 ? 1 : this._props.ScreenToLocalTransform().Scale ?? 1; const renderAnnotations = (childFilters?: () => string[]) => null; return ( @@ -1593,7 +1550,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps onPointerDown={async e => { e.button === 0 && !e.ctrlKey && e.stopPropagation(); }} - style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}> + style={{ transformOrigin: 'top left', transform: `scale(${scale})`, width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}> <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div> {renderAnnotations(this.opaqueFilter)} {SnappingManager.IsDragging ? null : renderAnnotations()} @@ -1639,7 +1596,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps </div> )} {this.routeToAnimate && ( - <div className="animation-panel"> + <div className="animation-panel" style={{ width: this.sidebarWidth() === 0 ? '100%' : `calc(100% - ${this.sidebarWidth()}px)` }}> <div id="route-to-animate-title">{StrCast(this.routeToAnimate.title)}</div> <div className="route-animation-options">{this.getRouteAnimationOptions()}</div> </div> @@ -1683,15 +1640,15 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps <MapboxMap ref={this._mapRef} mapboxAccessToken={MAPBOX_ACCESS_TOKEN} - id="mapbox-map" - viewState={{ ...this.mapboxMapViewState, width: NumCast(this.layoutDoc._width) / scale, height: NumCast(this.layoutDoc._height) / scale }} + viewState={this.isAnimating ? undefined : { ...this.mapboxMapViewState, width: NumCast(this.layoutDoc._width), height: NumCast(this.layoutDoc._height) }} mapStyle={this.dataDoc.map_style ? StrCast(this.dataDoc.map_style) : 'mapbox://styles/mapbox/streets-v11'} style={{ - transformOrigin: 'center', - transform: `scale(${scale < 1 ? 1 : scale})`, + position: 'absolute', + top: 0, + left: 0, zIndex: '0', - width: '100%', - height: '100%', + width: NumCast(this.layoutDoc._width) * parscale, + height: NumCast(this.layoutDoc._height) * parscale, }} initialViewState={this.isAnimating ? undefined : this.mapboxMapViewState} onMove={this.onMapMove} diff --git a/src/client/views/nodes/MapBox/MapboxApiUtility.ts b/src/client/views/nodes/MapBox/MapboxApiUtility.ts index 011b6f72a..592330ac2 100644 --- a/src/client/views/nodes/MapBox/MapboxApiUtility.ts +++ b/src/client/views/nodes/MapBox/MapboxApiUtility.ts @@ -34,48 +34,39 @@ export class MapboxApiUtility { static getDirections = async (origin: number[], destination: number[]): Promise<Record<TransportationType, any> | undefined> => { try { - const drivingQuery = await fetch( - `${MAPBOX_DIRECTIONS_BASE_URL}/driving/${origin[0]},${origin[1]};${destination[0]},${destination[1]}?steps=true&geometries=geojson&access_token=${MAPBOX_ACCESS_TOKEN}`); - const cyclingQuery = await fetch( - `${MAPBOX_DIRECTIONS_BASE_URL}/cycling/${origin[0]},${origin[1]};${destination[0]},${destination[1]}?steps=true&geometries=geojson&access_token=${MAPBOX_ACCESS_TOKEN}`); + const directionsPromises: Promise<any>[] = []; + const transportationTypes: TransportationType[] = ['driving', 'cycling', 'walking']; - const walkingQuery = await fetch( - `${MAPBOX_DIRECTIONS_BASE_URL}/walking/${origin[0]},${origin[1]};${destination[0]},${destination[1]}?steps=true&geometries=geojson&access_token=${MAPBOX_ACCESS_TOKEN}`); + transportationTypes.forEach((type) => { + directionsPromises.push( + fetch( + `${MAPBOX_DIRECTIONS_BASE_URL}/${type}/${origin[0]},${origin[1]};${destination[0]},${destination[1]}?steps=true&geometries=geojson&access_token=${MAPBOX_ACCESS_TOKEN}` + ).then((response) => response.json()) + ); + }); - const drivingJson = await drivingQuery.json(); - const cyclingJson = await cyclingQuery.json(); - const walkingJson = await walkingQuery.json(); - - console.log("Driving: ", drivingJson); - console.log("Cycling: ", cyclingJson); - console.log("Waling: ", walkingJson); - - const routeMap = { - 'driving': drivingJson.routes[0], - 'cycling': cyclingJson.routes[0], - 'walking': walkingJson.routes[0] - } + const results = await Promise.all(directionsPromises); const routeInfoMap: Record<TransportationType, any> = { 'driving': {}, 'cycling': {}, 'walking': {}, - }; - - Object.entries(routeMap).forEach(([key, routeData]) => { - const transportationTypeKey = key as TransportationType; - const geometry = routeData.geometry; - const coordinates = geometry.coordinates; - - console.log(coordinates); - - routeInfoMap[transportationTypeKey] = { + }; + + transportationTypes.forEach((type, index) => { + const routeData = results[index].routes[0]; + if (routeData) { + const geometry = routeData.geometry; + const coordinates = geometry.coordinates; + + routeInfoMap[type] = { duration: this.secondsToMinutesHours(routeData.duration), distance: this.metersToMiles(routeData.distance), - coordinates: coordinates + coordinates: coordinates, + }; } - }) + }); return routeInfoMap; @@ -102,4 +93,47 @@ export class MapboxApiUtility { return `${parseFloat((meters/1609.34).toFixed(2))} mi`; } -}
\ No newline at end of file +} + +// const drivingQuery = await fetch( +// `${MAPBOX_DIRECTIONS_BASE_URL}/driving/${origin[0]},${origin[1]};${destination[0]},${destination[1]}?steps=true&geometries=geojson&access_token=${MAPBOX_ACCESS_TOKEN}`); + +// const cyclingQuery = await fetch( +// `${MAPBOX_DIRECTIONS_BASE_URL}/cycling/${origin[0]},${origin[1]};${destination[0]},${destination[1]}?steps=true&geometries=geojson&access_token=${MAPBOX_ACCESS_TOKEN}`); + +// const walkingQuery = await fetch( +// `${MAPBOX_DIRECTIONS_BASE_URL}/walking/${origin[0]},${origin[1]};${destination[0]},${destination[1]}?steps=true&geometries=geojson&access_token=${MAPBOX_ACCESS_TOKEN}`); + +// const drivingJson = await drivingQuery.json(); +// const cyclingJson = await cyclingQuery.json(); +// const walkingJson = await walkingQuery.json(); + +// console.log("Driving: ", drivingJson); +// console.log("Cycling: ", cyclingJson); +// console.log("Waling: ", walkingJson); + +// const routeMap = { +// 'driving': drivingJson.routes[0], +// 'cycling': cyclingJson.routes[0], +// 'walking': walkingJson.routes[0] +// } + +// const routeInfoMap: Record<TransportationType, any> = { +// 'driving': {}, +// 'cycling': {}, +// 'walking': {}, +// }; + +// Object.entries(routeMap).forEach(([key, routeData]) => { +// const transportationTypeKey = key as TransportationType; +// const geometry = routeData.geometry; +// const coordinates = geometry.coordinates; + +// console.log(coordinates); + +// routeInfoMap[transportationTypeKey] = { +// duration: this.secondsToMinutesHours(routeData.duration), +// distance: this.metersToMiles(routeData.distance), +// coordinates: coordinates +// } +// })
\ No newline at end of file |
