aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAubrey-Li <63608597+Aubrey-Li@users.noreply.github.com>2021-08-21 13:52:34 -0700
committerAubrey-Li <63608597+Aubrey-Li@users.noreply.github.com>2021-08-21 13:52:34 -0700
commitbecf6eeac2bd370fef2eb67c107ccfaeef5805e4 (patch)
tree7296dca266d3ccf383ac6e8ef325f012f95fce5b
parentdb0682f46338277f4553a5a0ac8b83129237bfe5 (diff)
render map marker gps updates
-rw-r--r--src/client/documents/Documents.ts28
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx163
-rw-r--r--src/client/views/nodes/MapBox/MapMarker.tsx4
3 files changed, 149 insertions, 46 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 9740f81ef..c2d1fec51 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -156,6 +156,8 @@ export class DocumentOptions {
x?: number;
y?: number;
z?: number; // whether document is in overlay (1) or not (0 or undefined)
+ lat?: number;
+ lng?: number;
author?: string;
_layoutKey?: string;
type?: string;
@@ -726,8 +728,8 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.MAP), new List(documents), options);
}
- export function MapMarkerDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
- return InstanceFromProto(Prototypes.get(DocumentType.MARKER), new List(documents), options, id);
+ export function MapMarkerDocument(lat: number, lng: number, documents: Array<Doc>, options: DocumentOptions, id?: string) {
+ return InstanceFromProto(Prototypes.get(DocumentType.MARKER), new List(documents), {lat, lng, ...options}, id);
}
export function KVPDocument(document: Doc, options: DocumentOptions = {}) {
@@ -1325,6 +1327,25 @@ export namespace DocUtils {
return optionsCollection;
}
+ /**
+ *
+ * @param dms Degree Minute Second format exif gps data
+ * @param ref ref that determines negativity of decimal coordinates
+ * @returns a decimal format of gps latitude / longitude
+ */
+ function getDecimalfromDMS(dms?: number[], ref?: string) {
+ if (dms && ref) {
+ let degrees = dms[0] / dms[1];
+ let minutes = dms[2] / dms[3] / 60.0;
+ let seconds = dms[4] / dms[5] / 3600.0;
+
+ if (['S', 'W'].includes(ref)) {
+ degrees = -degrees; minutes = -minutes; seconds = -seconds
+ }
+ return (degrees + minutes + seconds).toFixed(5);
+ }
+ }
+
async function processFileupload(generatedDocuments: Doc[], name: string, type: string, result: Error | Upload.FileInformation, options: DocumentOptions) {
if (result instanceof Error) {
alert(`Upload failed: ${result.message}`);
@@ -1347,6 +1368,9 @@ export namespace DocUtils {
proto["data-nativeWidth"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
}
proto.contentSize = result.contentSize;
+ // exif gps data coordinates are stored in DMS (Degrees Minutes Seconds), the following operation converts that to decimal coordinates
+ proto.lat = getDecimalfromDMS(result.exifData?.data?.gps.GPSLatitude, result.exifData?.data?.gps.GPSLatitudeRef);
+ proto.lng = getDecimalfromDMS(result.exifData?.data?.gps.GPSLongitude, result.exifData?.data?.gps.GPSLongitudeRef);
}
generatedDocuments.push(doc);
}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 56203f3ae..e62f835b2 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,19 +1,24 @@
import { Autocomplete, GoogleMap, GoogleMapProps, InfoWindow, Marker } from '@react-google-maps/api';
-import { action, computed, IReactionDisposer, observable } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap } from 'mobx';
import { observer } from "mobx-react";
import * as React from "react";
-import { Doc, DocListCast, WidthSym } from '../../../../fields/Doc';
+import { DataSym, Doc, DocListCast, FieldsSym, WidthSym } from '../../../../fields/Doc';
import { documentSchema } from '../../../../fields/documentSchemas';
import { makeInterface } from '../../../../fields/Schema';
import { NumCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
+import { Docs } from '../../../documents/Documents';
import { DragManager } from '../../../util/DragManager';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
+import { AnchorMenu } from '../../pdf/AnchorMenu';
import { SidebarAnnos } from '../../SidebarAnnos';
import { StyleProp } from '../../StyleProvider';
import { FieldView, FieldViewProps } from '../FieldView';
import "./MapBox.scss";
import { MapMarker } from './MapMarker';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { identity } from 'lodash';
+import { Id } from '../../../../fields/FieldSymbols';
type MapDocument = makeInterface<[typeof documentSchema]>;
const MapDocument = makeInterface(documentSchema);
@@ -76,7 +81,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@observable private inputRef = React.createRef<HTMLInputElement>();
@observable private searchMarkers: google.maps.Marker[] = [];
@observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
- @observable private childDocs: MapMarker[] = [];
+ @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
+ @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.SidebarKey]); };
+ @computed get allMapMarkers() { return DocListCast(this.dataDoc[this.annotationKey]); };
+ @observable private allMarkers: Doc[] = [];
@observable _showSidebar = false;
@@ -96,17 +104,43 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this.searchBox = searchBox;
}
- // iterate childDocs to size, center, and zoom map to contain all markers
+ // iterate allMarkers to size, center, and zoom map to contain all markers
private fitBounds = (map: google.maps.Map) => {
console.log('map bound is:' + this.bounds);
- this.childDocs.map(place => {
- this.bounds.extend(place._latlngLocation);
+ this.allMarkers.map(place => {
+ this.bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) });
return place._markerId;
});
map.fitBounds(this.bounds)
}
- // store a reference to google map instance; fit map bounds to contain all markers
+
+ private hasGeolocation = (doc: Doc) => {
+ return doc.type === DocumentType.IMG
+
+ }
+
+
+ /**
+ * A function that examines allMapMarkers docs in the map node and form MapMarkers
+ */
+ private fillMarkers = () => {
+ this.allMapMarkers?.forEach(doc => {
+ // search for if the map marker exists, else create marker
+ if (doc.lat !== undefined && doc.lng !== undefined) {
+ const marker = Docs.Create.MapMarkerDocument(NumCast(doc.lat), NumCast(doc.lng), [], {})
+ this.allMarkers.push(marker)
+ }
+ })
+ }
+
+
+ /**
+ * store a reference to google map instance
+ * setup the drawing manager on the top right corner of map
+ * fit map bounds to contain all markers
+ * @param map
+ */
@action
private loadHandler = (map: google.maps.Map) => {
this._map = map;
@@ -125,11 +159,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
alert("Your geolocation is not supported by browser.")
}
this.fitBounds(map);
+ this.fillMarkers();
}
@action
- private markerLoadHandler = (marker: google.maps.Marker, place: MapMarker) => {
- place._markerId ? this.markerMap[place._markerId] = marker : null;
+ private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => {
+ place[Id] ? this.markerMap[place[Id]] = marker : null;
}
@action
@@ -156,7 +191,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
*/
sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
if (!this.layoutDoc._showSidebar) this.toggleSidebar();
- return this.addDocument(doc, sidebarKey);
+ const docs = doc instanceof Doc ? [doc] : doc
+ docs.forEach(doc => {
+ if (doc.lat !== undefined && doc.lng !== undefined) {
+ const marker = Docs.Create.MapMarkerDocument(NumCast(doc.lat), NumCast(doc.lng), [], {})
+ this.addDocument(marker, this.annotationKey)
+ }
+ }) //add to annotation list
+ return this.addDocument(doc, sidebarKey); // add to sidebar list
}
/**
@@ -183,6 +225,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@computed get sidebarColor() { return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "#e4e4e4")); }
+ /**
+ * function that reads the place inputed from searchbox, then zoom in on the location that's been autocompleted;
+ * add a customized temporary marker on the map
+ */
@action
private handlePlaceChanged = () => {
console.log(this.searchBox);
@@ -228,6 +274,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
)
}
+
@action
private handleInfoWindowClose = () => {
if (this.infoWindowOpen) {
@@ -246,6 +293,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
public get SidebarKey() { return this.fieldKey + "-sidebar"; }
+
@computed get sidebarHandle() {
const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length;
return (!annotated && !this.isContentActive()) ? (null) : <div className="mapBox-sidebar-handle" onPointerDown={this.sidebarDown}
@@ -254,6 +302,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
background: this.props.styleProvider?.(this.rootDoc, this.props as any, StyleProp.WidgetColor + (annotated ? ":annotated" : ""))
}} />;
}
+
@action
toggleSidebar = () => {
const prevWidth = this.sidebarWidth();
@@ -271,6 +320,65 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return false;
}
+ // TODO what's the difference between savedAnnotations & allMapMarkers?
+ getAnchor = () => {
+ const anchor =
+ AnchorMenu.Instance?.GetAnchor(this._savedAnnotations) ??
+ this.rootDoc // if anchormenu pops up else return rootDoc (map)
+ // Docs.Create.MapMarkerDocument(this.allMapMarkers, {
+ // title: StrCast(this.rootDoc.title + " " + this.layoutDoc._scrollTop),
+ // annotationOn: this.rootDoc,
+ // y: NumCast(this.layoutDoc._scrollTop),
+ // });
+ // this.addDocument(anchor);
+ return anchor;
+ }
+
+ private saveMarkerInfo = () => {
+
+ }
+ // create marker prop --> func that
+ private renderMarkers = () => {
+ this.allMarkers.map(place => (
+ <Marker
+ key={place[Id]}
+ position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }}
+ onLoad={marker => this.markerLoadHandler(marker, place)}
+ onClick={e => this.markerClickHandler(e, place)}
+ />
+ ))
+ }
+
+ private renderInfoWindow = (marker: MapMarker) => {
+ return this.infoWindowOpen && this.selectedPlace && (
+ <InfoWindow
+ anchor={this.markerMap[this.selectedPlace._markerId!]}
+ onCloseClick={this.handleInfoWindowClose}
+ >
+ <div style={{ backgroundColor: 'white', opacity: 0.75, padding: 12 }}>
+ <div style={{ fontSize: 16 }}>
+ <div>
+ {// TODO need to figure out how to render these childDocs of the MapMarker in InfoWindow
+ marker.childDocs}
+ <hr />
+ <form>
+ <label>Title: </label><br />
+ <input type="text" id="title" name="title"></input><br />
+ <label>Desription: </label><br />
+ <textarea style={{ height: 150 }} id="description" name="description" placeholder="Notes, a short description of this location, a brief comment, etc."></textarea>
+ <button type="submit">Save</button>
+ </form>
+ <hr />
+ <div>
+ <button>New link+</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </InfoWindow>
+ )
+ }
+
render() {
return <div className="mapBox" ref={this._ref}
style={{ pointerEvents: this.isContentActive() ? undefined : "none" }} >
@@ -295,39 +403,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
<input ref={this.inputRef} className="searchbox" type="text" placeholder="Search anywhere:" />
</Autocomplete>
- {this.childDocs.map(place => (
- <Marker
- key={place._markerId}
- position={place._latlngLocation}
- onLoad={marker => this.markerLoadHandler(marker, place)}
- onClick={e => this.markerClickHandler(e, place)}
- />
- ))}
- {this.infoWindowOpen && this.selectedPlace && (
- <InfoWindow
- anchor={this.markerMap[this.selectedPlace._markerId!]}
- onCloseClick={this.handleInfoWindowClose}
- >
- <div style={{ backgroundColor: 'white', opacity: 0.75, padding: 12 }}>
- <div style={{ fontSize: 16 }}>
- <div>
- <img src="http://placekitten.com/200/300" />
- <hr />
- <form>
- <label>Title: </label><br />
- <input type="text" id="fname" name="fname"></input><br />
- <label>Desription: </label><br />
- <textarea style={{ height: 150 }} id="lname" name="lname" placeholder="Notes, a short description of this location, a brief comment, etc."></textarea>
- </form>
- <hr />
- <div>
- <button>New link+</button>
- </div>
- </div>
- </div>
- </div>
- </InfoWindow>
- )}
+ {this.renderMarkers}
+ {this.renderInfoWindow}
</GoogleMap>
</div>
{/* {/* </LoadScript > */}
diff --git a/src/client/views/nodes/MapBox/MapMarker.tsx b/src/client/views/nodes/MapBox/MapMarker.tsx
index 34057cf48..fbad0cf65 100644
--- a/src/client/views/nodes/MapBox/MapMarker.tsx
+++ b/src/client/views/nodes/MapBox/MapMarker.tsx
@@ -17,7 +17,6 @@ import { AnchorMenu } from "../../pdf/AnchorMenu";
import { FieldView, FieldViewProps } from "../FieldView";
import { FormattedTextBox } from "../formattedText/FormattedTextBox";
import { RichTextMenu } from "../formattedText/RichTextMenu";
-import { PresMovement } from "../PresBox";
type MarkerDocument = makeInterface<[typeof documentSchema]>;
const MarkerDocument = makeInterface(documentSchema);
@@ -27,6 +26,7 @@ export type Coordinates = {
lng: number,
}
+//TODO: MapMarkerBox
@observer
export class MapMarker extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, MarkerDocument>(MarkerDocument) {
makeLinkAnchor(arg1: string, undefined: undefined, arg3: string) {
@@ -52,6 +52,8 @@ export class MapMarker extends ViewBoxAnnotatableComponent<ViewBoxAnnotatablePro
return tagDocs;
}
+ @computed get lat() { return NumCast(this.dataDoc.lat) }
+ @computed get lng() { return NumCast(this.dataDoc.lng) }
/**
* Methods