// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; // import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api'; // import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx'; // import { observer } from 'mobx-react'; // import * as React from 'react'; // import { Doc, DocListCast, Opt } from '../../../../fields/Doc'; // import { Id } from '../../../../fields/FieldSymbols'; // import { NumCast, StrCast } from '../../../../fields/Types'; // import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils'; // import { Docs } from '../../../documents/Documents'; // import { DragManager } from '../../../util/DragManager'; // import { SnappingManager } from '../../../util/SnappingManager'; // import { UndoManager } from '../../../util/UndoManager'; // import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; // import { ViewBoxAnnotatableComponent } from '../../DocComponent'; // import { Colors } from '../../global/globalEnums'; // import { AnchorMenu } from '../../pdf/AnchorMenu'; // import { Annotation } from '../../pdf/Annotation'; // import { SidebarAnnos } from '../../SidebarAnnos'; // import { FieldView, FieldViewProps } from '../FieldView'; // import { PinProps } from '../trails'; // import './MapBox2.scss'; // import { MapBoxInfoWindow } from './MapBoxInfoWindow'; // /** // * MapBox2 architecture: // * Main component: MapBox2.tsx // * Supporting Components: SidebarAnnos, CollectionStackingView // * // * MapBox2 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 MapBox2 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; // const mapContainerStyle = { // height: '100%', // }; // const defaultCenter = { // lat: 42.360081, // lng: -71.058884, // }; // const mapOptions = { // fullscreenControl: false, // }; // const apiKey = process.env.GOOGLE_MAPS; // const script = document.createElement('script'); // script.defer = true; // script.async = true; // script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`; // console.log(script.src); // document.head.appendChild(script); // /** // * Consider integrating later: allows for drawing, circling, making shapes on map // */ // // const drawingManager = new window.google.maps.drawing.DrawingManager({ // // drawingControl: true, // // drawingControlOptions: { // // position: google.maps.ControlPosition.TOP_RIGHT, // // 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, // // ], // // }, // // }); // // 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, // types: ['establishment'], // type pf places, subject of change according to user need // } as google.maps.places.AutocompleteOptions; // @observer // export class MapBox2 extends ViewBoxAnnotatableComponent>() { // private _dropDisposer?: DragManager.DragDropDisposer; // private _disposers: { [name: string]: IReactionDisposer } = {}; // private _annotationLayer: React.RefObject = React.createRef(); // @observable private _overlayAnnoInfo: Opt = undefined; // showInfo = action((anno: Opt) => (this._overlayAnnoInfo = anno)); // public static LayoutString(fieldKey: string) { // return FieldView.LayoutString(MapBox2, fieldKey); // } // public get SidebarKey() { // return this.fieldKey + '_sidebar'; // } // private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt) => void); // @computed get inlineTextAnnotations() { // return this.allMapMarkers.filter(a => a.text_inlineAnnotations); // } // @observable private _map: google.maps.Map = null as unknown as google.maps.Map; // @observable private selectedPlace: Doc | undefined = undefined; // @observable private markerMap: { [id: string]: google.maps.Marker } = {}; // @observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter; // @observable private inputRef = React.createRef(); // @observable private searchMarkers: google.maps.Marker[] = []; // @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options); // @observable private _savedAnnotations = new ObservableMap(); // @computed get allSidebarDocs() { // return DocListCast(this.dataDoc[this.SidebarKey]); // } // @computed get allMapMarkers() { // return DocListCast(this.dataDoc[this.annotationKey]); // } // @observable private toggleAddMarker = false; // @observable _showSidebar = false; // @computed get SidebarShown() { // return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false; // } // static _canAnnotate = true; // static _hadSelection: boolean = false; // private _sidebarRef = React.createRef(); // private _ref: React.RefObject = React.createRef(); // componentDidMount() { // this._props.setContentView?.(this); // } // @action // private setSearchBox = (searchBox: any) => { // this.searchBox = searchBox; // }; // // iterate allMarkers to size, center, and zoom map to contain all markers // private fitBounds = (map: google.maps.Map) => { // const curBounds = map.getBounds ?? new window.google.maps.LatLngBounds(); // const isFitting = this.allMapMarkers.reduce((fits, place) => fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean); // !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) => bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds())); // }; // /** // * Custom control for add marker button // * @param controlDiv // * @param map // */ // private CenterControl = () => { // const controlDiv = document.createElement('div'); // controlDiv.className = 'MapBox2-addMarker'; // // Set CSS for the control border. // const controlUI = document.createElement('div'); // controlUI.style.backgroundColor = '#fff'; // controlUI.style.borderRadius = '3px'; // controlUI.style.cursor = 'pointer'; // controlUI.style.marginTop = '10px'; // controlUI.style.borderRadius = '4px'; // controlUI.style.marginBottom = '22px'; // controlUI.style.textAlign = 'center'; // controlUI.style.position = 'absolute'; // controlUI.style.width = '32px'; // controlUI.style.height = '32px'; // controlUI.title = 'Click to toggle marker mode. In marker mode, click on map to place a marker.'; // const plIcon = document.createElement('img'); // plIcon.src = 'https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/add-256.png'; // plIcon.style.color = 'rgb(25,25,25)'; // plIcon.style.fontFamily = 'Roboto,Arial,sans-serif'; // plIcon.style.fontSize = '16px'; // plIcon.style.lineHeight = '32px'; // plIcon.style.left = '18'; // plIcon.style.top = '15'; // plIcon.style.position = 'absolute'; // plIcon.width = 14; // plIcon.height = 14; // plIcon.innerHTML = 'Add'; // controlUI.appendChild(plIcon); // // Set CSS for the control interior. // const markerIcon = document.createElement('img'); // markerIcon.src = 'https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-1024.png'; // markerIcon.style.color = 'rgb(25,25,25)'; // markerIcon.style.fontFamily = 'Roboto,Arial,sans-serif'; // markerIcon.style.fontSize = '16px'; // markerIcon.style.lineHeight = '32px'; // markerIcon.style.left = '-2'; // markerIcon.style.top = '1'; // markerIcon.width = 30; // markerIcon.height = 30; // markerIcon.style.position = 'absolute'; // markerIcon.innerHTML = 'Add'; // controlUI.appendChild(markerIcon); // // Setup the click event listeners // controlUI.addEventListener('click', () => { // if (this.toggleAddMarker === true) { // this.toggleAddMarker = false; // console.log('add marker button status:' + this.toggleAddMarker); // controlUI.style.backgroundColor = '#fff'; // markerIcon.style.color = 'rgb(25,25,25)'; // } else { // this.toggleAddMarker = true; // console.log('add marker button status:' + this.toggleAddMarker); // controlUI.style.backgroundColor = '#4476f7'; // markerIcon.style.color = 'rgb(255,255,255)'; // } // }); // controlDiv.appendChild(controlUI); // return controlDiv; // }; // /** // * Place the marker on google maps & store the empty marker as a MapMarker Document in allMarkers list // * @param position - the LatLng position where the marker is placed // * @param map // */ // @action // private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => { // const marker = new google.maps.Marker({ // position: position, // map: map, // }); // map.panTo(position); // const mapMarker = Docs.Create.PushpinDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {}); // this.addDocument(mapMarker, this.annotationKey); // }; // _loadPending = true; // /** // * 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; // this._loadPending = true; // const centerControlDiv = this.CenterControl(); // map.controls[google.maps.ControlPosition.TOP_RIGHT].push(centerControlDiv); // //drawingManager.setMap(map); // // if (navigator.geolocation) { // // navigator.geolocation.getCurrentPosition( // // (position: Position) => { // // const pos = { // // lat: position.coords.latitude, // // lng: position.coords.longitude, // // }; // // this._map.setCenter(pos); // // } // // ); // // } else { // // alert("Your geolocation is not supported by browser.") // // }; // map.setZoom(NumCast(this.dataDoc.map_zoom, 2.5)); // map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng))); // setTimeout(() => { // if (this._loadPending && this._map.getBounds) { // this._loadPending = false; // this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map); // } // }, 250); // // listener to addmarker event // this._map.addListener('click', (e: MouseEvent) => { // if (this.toggleAddMarker === true) { // this.placeMarker((e as any).latLng, map); // } // }); // }; // @action // centered = () => { // if (this._loadPending && this._map.getBounds) { // this._loadPending = false; // this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map); // } // this.dataDoc.mapLat = this._map.getCenter()?.lat(); // this.dataDoc.mapLng = this._map.getCenter()?.lng(); // }; // @action // zoomChanged = () => { // if (this._loadPending && this._map.getBounds) { // this._loadPending = false; // this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map); // } // this.dataDoc.map_zoom = this._map.getZoom(); // }; // /** // * 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; // }; // /** // * 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: google.maps.MapMouseEvent, place: Doc) => { // // set which place was clicked // this.selectedPlace = place; // place.infoWindowOpen = true; // }; // /** // * 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 // */ // sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => { // console.log('print all sidebar Docs'); // if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar(); // const docs = doc instanceof Doc ? [doc] : doc; // docs.forEach(doc => { // if (doc.lat !== undefined && doc.lng !== undefined) { // const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng); // if (existingMarker) { // Doc.AddDocToList(existingMarker, 'data', doc); // } else { // const marker = Docs.Create.PushpinDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {}); // this.addDocument(marker, this.annotationKey); // } // } // }); //add to annotation list // 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._layout_showSidebar) this.toggleSidebar(); // const docs = doc instanceof Doc ? [doc] : doc; // return this.removeDocument(doc, sidebarKey); // }; // /** // * Toggle sidebar onclick the tiny comment button on the top right corner // * @param e // */ // sidebarBtnDown = (e: React.PointerEvent) => { // setupMoveUpEvents( // this, // e, // (e, down, delta) => // runInAction(() => { // const localDelta = this._props // .ScreenToLocalTransform() // .scale(this._props.NativeDimScaling?.() || 1) // .transformDirection(delta[0], delta[1]); // const fullWidth = NumCast(this.layoutDoc._width); // const mapWidth = fullWidth - this.sidebarWidth(); // if (this.sidebarWidth() + localDelta[0] > 0) { // this._showSidebar = true; // this.layoutDoc._width = fullWidth + localDelta[0]; // this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%'; // } else { // this._showSidebar = false; // this.layoutDoc._width = mapWidth; // this.layoutDoc._layout_sidebarWidthPercent = '0%'; // } // return false; // }), // emptyFunction, // () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map') // ); // }; // sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this._props.PanelWidth(); // @computed get layout_sidebarWidthPercent() { // return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%'); // } // @computed get sidebarColor() { // return StrCast(this.layoutDoc.sidebar_color, 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 = () => { // const place = this.searchBox.getPlace(); // if (!place.geometry || !place.geometry.location) { // // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed // window.alert("No details available for input: '" + place.name + "'"); // return; // } // // zoom in on the location of the search result // if (place.geometry.viewport) { // this._map.fitBounds(place.geometry.viewport); // } else { // this._map.setCenter(place.geometry.location); // this._map.setZoom(17); // } // // customize icon => customized icon for the nature of the location selected // const icon = { // url: place.icon as string, // size: new google.maps.Size(71, 71), // origin: new google.maps.Point(0, 0), // anchor: new google.maps.Point(17, 34), // scaledSize: new google.maps.Size(25, 25), // }; // // put temporary cutomized marker on searched location // this.searchMarkers.forEach(marker => { // marker.setMap(null); // }); // this.searchMarkers = []; // this.searchMarkers.push( // new window.google.maps.Marker({ // map: this._map, // icon, // title: place.name, // position: place.geometry.location, // }) // ); // }; // /** // * Handles toggle of sidebar on click the little comment button // */ // @computed get sidebarHandle() { // return ( //
// //
// ); // } // // TODO: Adding highlight box layer to Maps // @action // toggleSidebar = () => { // //1.2 * w * ? = .2 * w .2/1.2 // const prevWidth = this.sidebarWidth(); // this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%'; // this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth); // }; // sidebarDown = (e: React.PointerEvent) => { // setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true); // }; // sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => { // const bounds = this._ref.current!.getBoundingClientRect(); // this.layoutDoc._layout_sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%'; // this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%'; // e.preventDefault(); // return false; // }; // setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func); // addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => { // return this.addDocument(doc, annotationKey); // }; // pointerEvents = () => { // return this._props.isContentActive() === false ? 'none' : this._props.isContentActive() && this._props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.IsDragging ? undefined : 'none'; // }; // @computed get annotationLayer() { // return ( //
// {this.inlineTextAnnotations // .sort((a, b) => NumCast(a.y) - NumCast(b.y)) // .map(anno => ( // // ))} //
// ); // } // getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.Document; // /** // * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker // * @returns // */ // private renderMarkers = () => { // return this.allMapMarkers.map(place => ( // this.markerLoadHandler(marker, place)} onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)} /> // )); // }; // // TODO: auto center on select a document in the sidebar // private handleMapCenter = (map: google.maps.Map) => { // // console.log("print the selected views in Document.Selected:") // // if (DocumentView.Selected().lastElement()) { // // console.log(DocumentView.Selected().lastElement()); // // } // }; // panelWidth = () => this._props.PanelWidth() / (this._props.NativeDimScaling?.() || 1) - this.sidebarWidth(); // panelHeight = () => this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1); // scrollXf = () => this.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop)); // transparentFilter = () => [...this._props.childFilters(), Utils.TransparentBackgroundFilter]; // opaqueFilter = () => [...this._props.childFilters(), Utils.OpaqueBackgroundFilter]; // infoWidth = () => this._props.PanelWidth() / 5; // infoHeight = () => this._props.PanelHeight() / 5; // anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick; // savedAnnotations = () => this._savedAnnotations; // get MicrosoftMaps() { // return (window as any).Microsoft.Maps; // } // render() { // const renderAnnotations = (childFilters?: () => string[]) => null; // return ( //
//
e.stopPropagation()} // onPointerDown={async e => { // e.button === 0 && !e.ctrlKey && e.stopPropagation(); // }} // style={{ width: `calc(100% - ${this.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}> //
{renderAnnotations(this.transparentFilter)}
// {renderAnnotations(this.opaqueFilter)} // {SnappingManager.IsDragging ? null : renderAnnotations()} // {this.annotationLayer} //
// // // e.stopPropagation()} placeholder="Enter location" /> // // {this.renderMarkers()} // {this.allMapMarkers // .filter(marker => marker.infoWindowOpen) // .map(marker => ( // // ))} // {/* {this.handleMapCenter(this._map)} */} // //
//
// {/* */} //
// //
// {this.sidebarHandle} //
// ); // } // }