From bc23855777633dfd1caf7237b75c1e8fee88dff4 Mon Sep 17 00:00:00 2001
From: zaultavangar <zaul_tavangar@brown.edu>
Date: Mon, 11 Dec 2023 20:37:52 -0500
Subject: new updates to map feature

---
 src/client/views/nodes/MapBox/MapBox.tsx | 288 +++++++++++++++++++++++--------
 1 file changed, 214 insertions(+), 74 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

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
-- 
cgit v1.2.3-70-g09d2

