diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/nodes/MapBox/MapBox.tsx | 96 | ||||
-rw-r--r-- | src/client/views/nodes/MapBox/MapMarker.tsx | 117 |
2 files changed, 58 insertions, 155 deletions
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 9cee7f2a2..6894cea19 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Autocomplete, GoogleMap, GoogleMapProps, InfoWindow, Marker } from '@react-google-maps/api'; +import { Autocomplete, GoogleMap, GoogleMapProps, InfoWindow, LoadScript, Marker } from '@react-google-maps/api'; import { action, computed, IReactionDisposer, observable, ObservableMap } from 'mobx'; import { observer } from "mobx-react"; import * as React from "react"; @@ -29,22 +29,25 @@ import { SidebarAnnos } from '../../SidebarAnnos'; import { StyleProp } from '../../StyleProvider'; import { FieldView, FieldViewProps } from '../FieldView'; import "./MapBox.scss"; -import { MapMarker } from './MapMarker'; -const _global = (window /* browser */ || global /* node */) as any; + +/** + * MapBox architecture: + * Main component: MapBox.tsx + * Supporting Components: SidebarAnnos, CollectionStackingView + * + * MapBox is a node that extends the ViewBoxAnnotatableComponent. Similar to PDFBox and WebBox, it supports interaction between sidebar content and document content. + * The main body of MapBox uses Google Maps API to allow location retrieval, adding map markers, pan and zoom, and open street view. + * Dash Document architecture is integrated with Maps API: When drag and dropping documents with ExifData (gps Latitude and Longitude information) available, + * sidebarAddDocument function checks if the document contains lat & lng information, if it does, then the document is added to both the sidebar and the infowindow (a pop up corresponding to a map marker--pin on map). + * The lat and lng field of the document is filled when importing (spec see ConvertDMSToDD method and processFileUpload method in Documents.ts). + * A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps + */ + +// const _global = (window /* browser */ || global /* node */) as any; type MapDocument = makeInterface<[typeof documentSchema]>; const MapDocument = makeInterface(documentSchema); -export type Coordinates = { - lat: number, - lng: number, -} - -export type LocationData = { - id: string; - pos: Coordinates; -}; - const mapContainerStyle = { height: '100%', }; @@ -58,6 +61,9 @@ const mapOptions = { fullscreenControl: false, } +/** + * Consider integrating later: allows for drawing, circling, making shapes on map + */ const drawingManager = new google.maps.drawing.DrawingManager({ drawingControl: true, drawingControlOptions: { @@ -65,13 +71,14 @@ const drawingManager = new google.maps.drawing.DrawingManager({ drawingModes: [ google.maps.drawing.OverlayType.MARKER, // currently we are not supporting the following drawing mode on map, a thought for future development - // google.maps.drawing.OverlayType.CIRCLE, - // google.maps.drawing.OverlayType.POLYLINE, + google.maps.drawing.OverlayType.CIRCLE, + google.maps.drawing.OverlayType.POLYLINE, ], }, }); +// options for searchbox in Google Maps Places Autocomplete API const options = { fields: ["formatted_address", "geometry", "name"], // note: level of details is charged by item per retrieval, not recommended to return all fields strictBounds: false, @@ -96,7 +103,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps // @observable private markerIdToMapMarker: { [id: string]: Doc | MapMarker | undefined } = {}; @observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter; @observable private zoom = 2.5; - @observable private infoWindowOpen = false; @observable private _marqueeing: number[] | undefined; @observable private _isAnnotating = false; @observable private bounds = new window.google.maps.LatLngBounds(); @@ -105,10 +111,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options); @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]); }; // method to add MapMarker to allMapMarkers - //TODO: change all markers to a filter function to change + @computed get allMapMarkers() { return DocListCast(this.dataDoc[this.annotationKey]); }; @observable private toggleAddMarker = false; - private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); @@ -241,18 +245,26 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps console.log(this.allMapMarkers) } }) - // this._map.addListener(drawingManager, 'markercomplete', this.addMarker) } + /** + * Load and render all map markers + * @param marker + * @param place + */ @action private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => { place[Id] ? this.markerMap[place[Id]] = marker : null; - // place[Id] ? this.markerIdToMapMarker[place[Id]] = place : null; console.log("the following is a markerMap from id to Marker:") console.log(this.markerMap); } + /** + * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true + * @param e + * @param place + */ @action private markerClickHandler = (e: MouseEvent, place: Doc) => { // set which place was clicked @@ -266,7 +278,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps } /** - * Called when dragging documents into map sidebar or directly into infowindow + * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts * @param doc * @param sidebarKey * @returns @@ -294,6 +306,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps return this.addDocument(doc, sidebarKey); // add to sidebar list } + /** + * Removing documents from the sidebar + * @param doc + * @param sidebarKey + * @returns + */ sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => { if (this.layoutDoc._showSidebar) this.toggleSidebar(); const docs = doc instanceof Doc ? [doc] : doc; @@ -386,6 +404,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps place.infoWindowOpen = false; } + /** + * Handles toggle of sidebar on click the little comment button + */ @computed get sidebarHandle() { TraceMobx(); const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length; @@ -403,6 +424,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps </div>; } + // TODO: Adding highlight box layer to Maps @action toggleSidebar = () => { const prevWidth = this.sidebarWidth(); @@ -459,20 +481,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps 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); + this.rootDoc return anchor; } infoWidth = () => this.props.PanelWidth() / 5; infoHeight = () => this.props.PanelWidth() / 5; - //documentView + // Collection stacking view for documents in the infowindow of a map marker private renderChildDocs = (selectedDoc: Doc) => { return <div style={{ width: this.infoWidth(), height: this.infoHeight() }}> <CollectionStackingView { @@ -521,6 +537,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps )) } + /** + * Renders infowindow corresponding to a map marker document + * @param place + * @returns + */ private renderInfoWindow = (place: Doc) => { return place.infoWindowOpen && ( @@ -540,6 +561,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps ) } + // TODO: auto center on select a document in the sidebar private handleMapCenter = (map: google.maps.Map) => { console.log("print the selected views in selectionManager:") if (SelectionManager.Views().lastElement()) { @@ -579,13 +601,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps addDocument={this.sidebarAddDocument} childPointerEvents={true} pointerEvents={CurrentUserUtils.SelectedTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />; - return <div className="mapBox" ref={this._ref} - // style={{ pointerEvents: this.isContentActive() ? undefined : "none" }} - > - {/* // {/* <LoadScript - // googleMapsApiKey={process.env.GOOGLE_MAPS!} - // libraries={['places', 'drawing']} - // > */} + return <div className="mapBox" ref={this._ref}> + {/* <LoadScript + googleMapsApiKey={process.env.GOOGLE_MAPS!} + libraries={['places', 'drawing']} + > */} <div className="mapBox-wrapper" onWheel={e => e.stopPropagation()} onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} @@ -627,7 +647,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} />} </div> - {/* {/* </LoadScript > */} + {/* </LoadScript > */} <div className="mapBox-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}> <SidebarAnnos ref={this._sidebarRef} diff --git a/src/client/views/nodes/MapBox/MapMarker.tsx b/src/client/views/nodes/MapBox/MapMarker.tsx deleted file mode 100644 index fbad0cf65..000000000 --- a/src/client/views/nodes/MapBox/MapMarker.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import { action, computed, IReactionDisposer, observable } from "mobx"; -import { observer } from "mobx-react"; -import { Transaction } from "prosemirror-state"; -import { EditorView } from "prosemirror-view"; -import * as React from "react"; -import { Doc, DocListCast, Opt } from "../../../../fields/Doc"; -import { documentSchema } from "../../../../fields/documentSchemas"; -import { Id } from "../../../../fields/FieldSymbols"; -import { createSchema, makeInterface } from "../../../../fields/Schema"; -import { Cast, NumCast } from "../../../../fields/Types"; -import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; -import { DragManager } from "../../../util/DragManager"; -import { CollectionViewType } from "../../collections/CollectionView"; -import { TabDocView } from "../../collections/TabDocView"; -import { ViewBoxAnnotatableProps, ViewBoxAnnotatableComponent } from "../../DocComponent"; -import { AnchorMenu } from "../../pdf/AnchorMenu"; -import { FieldView, FieldViewProps } from "../FieldView"; -import { FormattedTextBox } from "../formattedText/FormattedTextBox"; -import { RichTextMenu } from "../formattedText/RichTextMenu"; - -type MarkerDocument = makeInterface<[typeof documentSchema]>; -const MarkerDocument = makeInterface(documentSchema); - -export type Coordinates = { - lat: number, - lng: number, -} - -//TODO: MapMarkerBox -@observer -export class MapMarker extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, MarkerDocument>(MarkerDocument) { - makeLinkAnchor(arg1: string, undefined: undefined, arg3: string) { - throw new Error("Method not implemented."); - } - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MapMarker, fieldKey); } - private _markerRef: React.RefObject<google.maps.Marker> = React.createRef(); - private _disposers: { [name: string]: IReactionDisposer } = {}; - _latlngLocation!: Coordinates; - _markerId!: number; - private _editorView: Opt<EditorView> // we'll see if this becomes useful for marker annotation/create link - @observable _title: string = ""; // the title of the marker - @observable _description: string = ""; // the description of the marker contents - @observable isMarkerActive: boolean = false; // whether the marker is selected (we'll see if we need this) - @observable activeLinks: Doc[] = []; //TBD: what linking data structure looks like - @computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); } // a list of documents with the same/similar geographic coordinates - @computed get tagDocs() { // might come in handy for filtering - const tagDocs: Doc[] = []; - for (const doc of this.childDocs) { - const tagDoc = Cast(doc.presentationTargetDoc, Doc, null); - tagDocs.push(tagDoc); - } - return tagDocs; - } - - @computed get lat() { return NumCast(this.dataDoc.lat) } - @computed get lng() { return NumCast(this.dataDoc.lng) } - - /** - * Methods - */ - componentDidMount() { } - - componentWillMount() { } - - @computed private get filterAssociatedDocs() { - return - } - - addLinkToMarker = () => { } - - - - @action - setupAnchorMenu = () => { - AnchorMenu.Instance.Status = "marquee"; - AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => { - this._editorView?.state && RichTextMenu.Instance.insertHighlight(color, this._editorView.state, this._editorView?.dispatch); - return undefined; - }); - /** - * This function is used by the PDFmenu to create an anchor highlight and a new linked text annotation. - * It also initiates a Drag/Drop interaction to place the text annotation. - */ - AnchorMenu.Instance.StartDrag = action(async (e: PointerEvent, ele: HTMLElement) => { - e.preventDefault(); - e.stopPropagation(); - const targetCreator = (annotationOn?: Doc) => { - const target = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn); - FormattedTextBox.SelectOnLoad = target[Id]; - return target; - }; - - // DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docViewPath().lastElement(), this.getAnchor, targetCreator), e.pageX, e.pageY); - }); - const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to); - this.props.isSelected(true) && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom); - } - - // will see if end up using this - dispatchTransaction = (tx: Transaction) => { } - - - //will see if needed - // for inserting timestamps - insertTime = () => { } - - //for setting the title of the marker - @action - private updateTitle = () => { } - - //for updating the description of the marker - @action - private updateDescrption = () => { } - - - -}
\ No newline at end of file |