diff options
Diffstat (limited to 'src/client/views/nodes/MapBox/MapBox.tsx')
-rw-r--r-- | src/client/views/nodes/MapBox/MapBox.tsx | 288 |
1 files changed, 214 insertions, 74 deletions
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 2b563faf2..ac926e1fb 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -9,7 +9,7 @@ import * as React from 'react'; import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc'; import { DocCss, Highlight } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; -import { DocCast, NumCast, StrCast } from '../../../../fields/Types'; +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 { DocumentType } from '../../../documents/DocumentTypes'; @@ -52,11 +52,13 @@ import './MapBox.scss'; import { NumberLiteralType } from 'typescript'; // import { GeocoderControl } from './GeocoderControl'; import mapboxgl, { LngLat, MapLayerMouseEvent } from 'mapbox-gl'; -import { Feature, FeatureCollection } from 'geojson'; +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, TextField } from '@mui/material'; +import { Autocomplete, Checkbox, FormControlLabel, TextField } from '@mui/material'; import { List } from '../../../../fields/List'; +import { listSpec } from '../../../../fields/Schema'; +import { IconLookup, faGear } from '@fortawesome/free-solid-svg-icons'; // amongus /** @@ -140,15 +142,17 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps @computed get allRoutes() { return this.allAnnotations.filter(anno => anno.type === DocumentType.MAPROUTE); } - @computed get allRoutesGeoJson() { - const features = this.allRoutes.map(route => { + @computed get allRoutesGeoJson(): FeatureCollection { + const features: Feature<Geometry, GeoJsonProperties>[] = this.allRoutes.map(route => { + console.log("Route coords: ", route.coordinates); + const geometry: LineString = { + type: 'LineString', + coordinates: JSON.parse(StrCast(route.coordinates)) + } return { type: 'Feature', properties: {}, - geometry: { - type: 'LineString', - coordinates: route.coordinates - } + geometry: geometry }; }); @@ -771,7 +775,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps NumCast(longitude), false, [], - {title: location ?? `lat=${latitude},lng=${longitude}`, map: location, description: "", wikiData: wikiData}, + { + title: location ?? `lat=${latitude},lng=${longitude}`, + map: location, + description: "", + wikiData: wikiData, + markerType: 'MAPBOX_MARKER', + markerColor: '#3FB1CE' + }, // { title: map ?? `lat=${latitude},lng=${longitude}`, map: map }, // ,'pushpinIDamongus'+ this.incrementer++ ); @@ -783,11 +794,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps }, 'createpin'); @action - createMapRoute = undoable((coordinates: List<any>, origin: string, destination: string) => { + createMapRoute = undoable((coordinates: Position[], origin: string, destination: string) => { + console.log(coordinates); const mapRoute = Docs.Create.MapRouteDocument( false, [], - {title: `${origin} -> ${destination}`, routeCoordinates: coordinates}, + {title: `${origin} -> ${destination}`, routeCoordinates: JSON.stringify(coordinates)}, ); this.addDocument(mapRoute, this.annotationKey); return mapRoute; @@ -800,29 +812,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps - @observable - mapboxMapViewState: ViewState = { - zoom: 9, - longitude: -71.41, - latitude: 41.82, - pitch: 0, - bearing: 0, - padding: { - top: 0, - bottom: 0, - left: 0, - right: 0 - } - } + @observable featuresFromGeocodeResults: any[] = []; - @action - onMapMove = (e: ViewStateChangeEvent) => { - this.mapboxMapViewState = e.viewState; - } - @action addMarkerForFeature = (feature: any) => { @@ -838,6 +832,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps location, wikiData ) + + if (this._mapRef.current){ + this._mapRef.current.flyTo({ + center: feature.center + }); + } this.featuresFromGeocodeResults = []; } else { @@ -855,6 +855,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps const features = await MapboxApiUtility.forwardGeocodeForFeatures(searchText); if (features){ runInAction(() => { + this.settingsOpen= false; this.featuresFromGeocodeResults = features; }) } @@ -988,6 +989,75 @@ 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; + + @observable + mapStyle: string = 'mapbox://styles/mapbox/streets-v12' + + @observable + showTerrain: boolean = false; + + @action + toggleSettings = () => { + this.featuresFromGeocodeResults = []; + this.settingsOpen = !this.settingsOpen; + } + + @action + changeMapStyle = (e: React.ChangeEvent<HTMLSelectElement>) => { + this.mapStyle = `mapbox://styles/mapbox/${e.target.value}` + } + + @action + onMapMove = (e: ViewStateChangeEvent) => { + this.mapboxMapViewState = e.viewState; + } + + @action + toggleShowTerrain = () => { + this.showTerrain = !this.showTerrain; + } + + @action + onBearingChange = (e: React.ChangeEvent<HTMLInputElement>) => { + const newVal = parseInt(e.target.value) + if (!isNaN(newVal) && newVal >= 0){ + this.mapboxMapViewState = { + ...this.mapboxMapViewState, + bearing: parseInt(e.target.value) + } + } + } + + @action + onPitchChange = (e: React.ChangeEvent<HTMLInputElement>) => { + const newVal = parseInt(e.target.value); + if (!isNaN(newVal) && newVal >= 0){ + this.mapboxMapViewState = { + ...this.mapboxMapViewState, + pitch: parseInt(e.target.value) + } + } + } + + + static _firstRender = true; static _rerenderDelay = 500; @@ -1029,51 +1099,59 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps placeholder='Enter a location' onChange={(e) => this.handleSearchChange(e.target.value)} /> - {/* <Autocomplete - fullWidth - id="map-location-searcher" - freeSolo - onInputChange={(e, searchText) => this.handleSearchChange(searchText)} - onChange={(e, selectedOption) => { - this.handleSearchChange(""); // clear input - this.addMarkerForFeature(selectedOption); - }} - options={this.featuresFromGeocodeResults - .filter(feature => feature.place_name) - .map(feature => feature)} - getOptionLabel={(feature) => feature.place_name} - renderInput={(params) => ( - <TextField - {...params} - placeholder='Enter a location' - /> - )} - /> */} - {/* <EditableText - // editing - setVal={(newText: string | number) => typeof newText === 'string' && this.handleSearchChange(newText)} - // onEnter={e => this.bingSearch()} - onEnter={e => {}} - height={32} - // placeholder={this.bingSearchBarContents || 'Enter a location'} - placeholder='Enter a location' - textAlign="center" - /> */} - {/* <IconButton - icon={ - <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="magnifying-glass" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" color="#DFDFDF"> - <path - fill="currentColor" - d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"></path> - </svg> - } - onClick={this.bingSearch} + <IconButton + icon={<FontAwesomeIcon icon={faGear as IconLookup} size='1x'/>} type={Type.TERT} + onClick={(e) => this.toggleSettings()} + /> - <div style={{ width: 30, height: 30 }} ref={this._dragRef} onPointerDown={this.dragToggle}> - <Button tooltip="drag to place a pushpin" icon={<FontAwesomeIcon size={'lg'} icon={'bullseye'} />} /> - </div> */} </div> + {this.settingsOpen && + <div className='mapbox-settings-panel' style={{right: `${0+ this.sidebarWidth()}px`}}> + <div className='mapbox-style-select'> + <div> + Map Style: + </div> + <div> + <select onChange={this.changeMapStyle}> + <option value='streets-v12'>Streets</option> + <option value='outdoors-v12'>Outdoors</option> + <option value='light-v11'>Light</option> + <option value='dark-v11'>Dark</option> + <option value='satellite-v9'>Satellite</option> + <option value='satellite-streets-v12'>Satellite Streets</option> + <option value='navigation-day-v1'>Navigation Day</option> + <option value='navigation-night-v1'>Navigation Night</option> + </select> + </div> + </div> + <div className='mapbox-bearing-selection'> + <div>Bearing: </div> + <input + value={this.mapboxMapViewState.bearing} + min={0} + type='number' + onChange={this.onBearingChange}/> + </div> + <div className='mapbox-pitch-selection'> + <div>Pitch: </div> + <input + value={this.mapboxMapViewState.pitch} + min={0} + type='number' + onChange={this.onPitchChange}/> + </div> + <div className='mapbox-terrain-selection'> + <div>Show terrain: </div> + <input + type='checkbox' + checked={this.showTerrain} + onChange={this.toggleShowTerrain} + /> + </div> + </div> + } + <div className='mapbox-geocoding-search-results'> {this.featuresFromGeocodeResults.length > 0 && ( <React.Fragment> @@ -1107,15 +1185,24 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps }} mapboxAccessToken={MAPBOX_ACCESS_TOKEN} id="mapbox-map" - mapStyle="mapbox://styles/mapbox/streets-v9" + mapStyle={this.mapStyle} style={{height: '100%', width: '100%'}} {...this.mapboxMapViewState} onMove={this.onMapMove} onDblClick={this.handleMapClick} + terrain={this.showTerrain ? { source: 'mapbox-dem', exaggeration: 2.0 } : undefined} > + <Source + id="mapbox-dem" + type="raster-dem" + url="mapbox://mapbox.mapbox-terrain-dem-v1" + tileSize={512} + maxzoom={14} + /> <Source id='temporary-route' type='geojson' data={this.temporaryRouteSource}/> + <Source id='map-routes' type='geojson' data={this.allRoutesGeoJson}/> <Layer id='temporary-route-layer' type='line' @@ -1123,6 +1210,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps layout={{"line-join": "round", "line-cap": "round"}} paint={{"line-color": "#36454F", "line-width": 4, "line-dasharray": [1,1]}} /> + <Layer + id='map-routes-layer' + type='line' + source='map-routes' + layout={{"line-join": "round", "line-cap": "round"}} + paint={{"line-color": "#FF0000", "line-width": 4}} + /> + <> {this.allPushpins // .filter(anno => !anno.layout_unrendered) @@ -1219,3 +1314,48 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps ); } } + +{/* <Autocomplete + fullWidth + id="map-location-searcher" + freeSolo + onInputChange={(e, searchText) => this.handleSearchChange(searchText)} + onChange={(e, selectedOption) => { + this.handleSearchChange(""); // clear input + this.addMarkerForFeature(selectedOption); + }} + options={this.featuresFromGeocodeResults + .filter(feature => feature.place_name) + .map(feature => feature)} + getOptionLabel={(feature) => feature.place_name} + renderInput={(params) => ( + <TextField + {...params} + placeholder='Enter a location' + /> + )} + /> */} + {/* <EditableText + // editing + setVal={(newText: string | number) => typeof newText === 'string' && this.handleSearchChange(newText)} + // onEnter={e => this.bingSearch()} + onEnter={e => {}} + height={32} + // placeholder={this.bingSearchBarContents || 'Enter a location'} + placeholder='Enter a location' + textAlign="center" + /> */} + {/* <IconButton + icon={ + <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="magnifying-glass" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" color="#DFDFDF"> + <path + fill="currentColor" + d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"></path> + </svg> + } + onClick={this.bingSearch} + type={Type.TERT} + /> + <div style={{ width: 30, height: 30 }} ref={this._dragRef} onPointerDown={this.dragToggle}> + <Button tooltip="drag to place a pushpin" icon={<FontAwesomeIcon size={'lg'} icon={'bullseye'} />} /> + </div> */}
\ No newline at end of file |