import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { GoogleMapProps, Marker } from '@react-google-maps/api'; import BingMapsReact from 'bingmaps-react'; import { Button, EditableText, IconButton, Type } from 'browndash-components'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, Opt } from '../../../../fields/Doc'; import { Width } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { ScriptField } from '../../../../fields/ScriptField'; import { NumCast, StrCast } from '../../../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { DragManager } from '../../../util/DragManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoable, UndoManager } from '../../../util/UndoManager'; import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent'; import { Colors } from '../../global/globalEnums'; import { MarqueeAnnotator } from '../../MarqueeAnnotator'; import { Annotation } from '../../pdf/Annotation'; import { SidebarAnnos } from '../../SidebarAnnos'; import { DocumentView, OpenWhere } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { PinProps, PresBox } from '../trails'; import './MapBox.scss'; // amongus /** * 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; const mapContainerStyle = { height: '100%', }; const defaultCenter = { lat: 42.360081, lng: -71.058884, }; const mapOptions = { fullscreenControl: false, }; const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing Maps api key here: https://www.bingmapsportal.com/ and then add it to the .env file in the Dash-Web root directory as: _CLIENT_BING_MAPS= // const script = document.createElement('script'); // script.defer = true; // script.async = true; // script.src = `https://maps.googleapis.com/maps/api/js?key=${bingApiKey}&libraries=places,drawing`; // 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, // ], // }, // }); @observer export class MapBox extends ViewBoxAnnotatableComponent>() { private _dropDisposer?: DragManager.DragDropDisposer; private _disposers: { [name: string]: IReactionDisposer } = {}; private _annotationLayer: React.RefObject = React.createRef(); @observable private _overlayAnnoInfo: Opt; showInfo = action((anno: Opt) => (this._overlayAnnoInfo = anno)); public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MapBox, fieldKey); } public get SidebarKey() { return this.fieldKey + '_sidebar'; } private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => 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; @observable private markerMap: { [id: string]: google.maps.Marker } = {}; @observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter; @observable private _marqueeing: number[] | undefined; @observable private inputRef = React.createRef(); @observable private searchMarkers: google.maps.Marker[] = []; @observable private searchBox = undefined as any; // 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; private _mainCont: React.RefObject = React.createRef(); @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(); private _disposer: { [key: string]: IReactionDisposer } = {}; componentDidMount() { this.props.setContentView?.(this); } componentWillUnmount(): void { Object.keys(this._disposer).forEach(key => this._disposer[key]?.()); } // 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())); }; /** * 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; }; /** * 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; @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.mapZoom = this._map.getZoom(); }; /** * 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, }) ); }; /** * 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 = 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')); } /** * 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); }; createNoteAnnotation = () => { !this.layoutDoc.layout_showSidebar && this.toggleSidebar(); setTimeout(() => this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false))); // give time for sidebarRef to be created }; 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); @action onMarqueeDown = (e: React.PointerEvent) => { if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { setupMoveUpEvents( this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; return true; }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false ); } }; @action finishMarquee = (x?: number, y?: number) => { this._marqueeing = undefined; x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false); }; addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => { return this.addDocument(doc, annotationKey); }; pointerEvents = () => (this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : 'none'); @computed get annotationLayer() { return (
{this.inlineTextAnnotations .sort((a, b) => NumCast(a.y) - NumCast(b.y)) .map(anno => ( ))}
); } // Old get anchor function // getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc; /** * 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)} /> )); }; panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth(); panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop)); transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()]; opaqueFilter = () => [...this.props.childFilters(), Utils.IsOpaqueFilter()]; infoWidth = () => this.props.PanelWidth() / 5; infoHeight = () => this.props.PanelHeight() / 5; anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick; savedAnnotations = () => this._savedAnnotations; _bingSearchManager: any; _bingMap: any; get MicrosoftMaps() { return (window as any).Microsoft.Maps; } // uses Bing Search to retrieve lat/lng for a location. eg., // const results = this.geocodeQuery(map.map, 'Philadelphia, PA'); // to move the map to that location: // const location = await this.geocodeQuery(this._bingMap, 'Philadelphia, PA'); // this._bingMap.current.setView({ // mapTypeId: this.MicrosoftMaps.MapTypeId.aerial, // center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude), // }); // bingGeocode = (map: any, query: string) => { return new Promise<{ latitude: number; longitude: number }>((res, reject) => { //If search manager is not defined, load the search module. if (!this._bingSearchManager) { //Create an instance of the search manager and call the geocodeQuery function again. this.MicrosoftMaps.loadModule('Microsoft.Maps.Search', () => { this._bingSearchManager = new this.MicrosoftMaps.Search.SearchManager(map.current); res(this.bingGeocode(map, query)); }); } else { this._bingSearchManager.geocode({ where: query, callback: action((r: any) => { res(r.results[0].location); }), errorCallback: (e: any) => reject(), }); } }); }; /** * * * ERIC'S BING MAP CODE BELOW * * * **/ @observable bingSearchBarContents: any = 'enter city/zip/...'; // For Bing Maps: The contents of the Bing search bar (string) geoDataRequestOptions = { entityType: 'PopulatedPlace', }; // incrementer: number = 0; /* * Creates Pushpin doc and adds it to the list of annotations */ @action createPushpin = (latitude: number, longitude: number) => { // Stores the pushpin as a MapMarkerDocument const mapMarker = Docs.Create.PushpinDocument( NumCast(latitude), NumCast(longitude), false, [], {} // ,'pushpinIDamongus'+ this.incrementer++ ); this.addDocument(mapMarker, this.annotationKey); // mapMarker.infoWindowOpen = true; }; /* * Pushpin dblclick */ @action pushpinDblClicked = (pin: any, pinDoc?: Doc) => { if (pinDoc) this.removePushpin(pinDoc, pin); else this._bingMap.current.entities.remove(pin); }; // The pin that is selected selectedPin:Doc | undefined; /* * Pushpin onclick */ @action pushpinClicked = (pinDoc: Doc) => { // TODO: // if (sidebarannos is not open) open sidebarannos // creates button onclick removes the doc from annotations // pan to pushpin location // this.dataDoc.latitude = pinDoc.latitude; // this.dataDoc.longitude = pinDoc.longitude; this.selectedPin = pinDoc /// this should SELECT, not center this.createNoteAnnotation(); }; /** * Returns a list of Pushpin docs */ @computed get allMapPushpins() { return DocListCast(this.dataDoc[this.annotationKey]); } /** * Map OnClick ~> creates a pushpin */ @action mapOnClick = (e: { location: { latitude: any; longitude: any } }) => { if (this.placePinOn) { this.createPushpin(e.location.latitude, e.location.longitude); // this.addAllPins(); // this.placePinOn = false; } }; /* * Updates values of layout doc to match the current map */ @action updateLayout = () => { this.dataDoc.latitude = this._bingMap.current.getCenter().latitude; this.dataDoc.longitude = this._bingMap.current.getCenter().longitude; this.dataDoc.mapZoom = this._bingMap.current.getZoom(); // if(this.dataDoc.mapType == 'x'){ // this.dataDoc.locationToLookAt // } // this.dataDoc.mapType = new this.MicrosoftMaps.MapTypeId(); }; /* * Updates maptype */ @action updateMapType = () => { this.dataDoc.mapType = this._bingMap.current.getMapTypeId(); }; searched_pin: any; /* * For Bing Maps * Called by search button's onClick * Finds the geocode of the searched contents and sets location to that location **/ @action bingSearch = async () => { const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents); this.dataDoc.latitude = location.latitude; this.dataDoc.longitude = location.longitude; this.dataDoc.mapZoom = this._bingMap.current.getZoom(); // this.dataDoc.mapType = this._bingMap.current.getMapTypeId(); // this.bingSearchBarContents=location.name; // Centers on the searched location // this._bingMap.current.panTo(new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude)); // this._bingMap.current.setView({ // center: new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude), // }); // Creates a temporary pin but does not add it to the dataDoc var temp = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), { color: 'blue', }); this.MicrosoftMaps.Events.addHandler(temp, 'dblclick', (e: any) => this.pushpinDblClicked(temp)); if (temp != this.searched_pin || this.searched_pin == null) { this._bingMap.current.entities.push(temp); this.searched_pin = temp; } }; /** * Adds all pushpins in dataDoc onto the map (render) - OLD & UNUSED */ // @action // addAllPins = () => { // this._bingMap.current.entities.clear(); // if (this.searched_pin) this._bingMap.current.entities.push(this.searched_pin); // // this.allMapPushpins.map(pin => this.addPushpin(pin)); // }; /* * Returns doc w/ relevant info */ getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { /// this should use SELECTED pushpin for lat/long if there is a selection, otherwise CENTER const anchor = Docs.Create.MapanchorDocument({ title: 'MapAnchor:' + this.rootDoc.title, config_latitude: NumCast(this.selectedPin ? this.selectedPin.latitude : this.dataDoc.latitude), config_longitude: NumCast(this.selectedPin ? this.selectedPin.longitude : this.dataDoc.longitude), config_mapZoom: NumCast(this.dataDoc.mapZoom), config_mapType: StrCast(this.dataDoc.mapType), // preslocationToLookAt:this.dataDoc.locationToLookAt, // presType: layout_unrendered: true, annotationOn: this.rootDoc, }); if (anchor) { if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; /* addAsAnnotation &&*/ this.addDocument(anchor); PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.rootDoc); return anchor; } return this.rootDoc; }; /* * Input: pin doc * Adds MicrosoftMaps Pushpin to the map (render) */ @action addPushpin = (pin: Doc) => { const pushPin = pin.infoWindowOpen ? new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.latitude, pin.longitude), {}) : new this.MicrosoftMaps.Pushpin( new this.MicrosoftMaps.Location(pin.latitude, pin.longitude) // {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'} ); this._bingMap.current.entities.push(pushPin); this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin)); this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pushPin, pin)); }; @observable pinIsSelected_TEMPORARY: boolean = false; // toggles if remove pin button appears /* * Input: pin doc * Removes MicrosoftMaps Pushpin to the map (render) */ @action removePushpin = (pinDoc: Doc, pin: any) => { // this.allMapPushpins // this.allMapPushpins.map(pin => this.addPushpin(pin)); // this._bingMap.current.entities.clear(); this._bingMap.current.entities.remove(pin); this.removeDocument(pinDoc, this.annotationKey); // this.dataDoc[this.annotationKey] }; /** * View options for bing maps */ bingViewOptions = { center: { latitude: this.dataDoc.latitude ?? defaultCenter.lat, longitude: this.dataDoc.longitude ?? defaultCenter.lng }, zoom: this.dataDoc.latitude ?? 10, mapTypeId: 'grayscale', }; /** * Map options */ bingMapOptions = { navigationBarMode: 'square', backgroundColor: '#f1f3f4', enableInertia: true, supportedMapTypes: ['grayscale', 'canvasLight'], disableMapTypeSelectorMouseOver: true, // showScalebar:true // disableRoadView:true, // disableBirdseye:true streetsideOptions: { showProblemReporting: false, showCurrentAddress: false, }, }; /** * Toggles mode for placing a pin down on map */ @observable placePinOn = false; @action togglePlacePin = () => { if (this.placePinOn) this.placePinOn = false; else this.placePinOn = true; }; @action searchbarOnEdit = (newText: string) => { this.bingSearchBarContents = newText; }; /* * Called when BingMap is first rendered * Initializes starting values */ @observable _mapReady = false; @action bingMapReady = (map: any) => { this._mapReady = true; this._bingMap = map.map; if (!this._bingMap.current) { alert('NO Map!?'); } this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', undoable(this.mapOnClick, 'Added Pin to Map')); this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', undoable(this.updateLayout, 'Map Layout Change')); this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, 'Map ViewType Change')); //this.updateLayout(); // this.updateMapType(); this._disposer.location = reaction( () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.mapZoom, mapType: this.rootDoc.mapType }), locationObject => { // if (this._bingMap.current) this._bingMap.current?.setView({ mapTypeId: locationObject.mapType, zoom: locationObject.zoom, center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng), }); }, { fireImmediately: true } ); }; // Keeps track of when dragging a pin onto map draggingPin = false; dragToggle = (e: React.PointerEvent) => { console.log('DRAGGING TOGGLE'); document.addEventListener('drop', this.dropPin, true); document.addEventListener('pointermove', this.pinMove, true); e.stopPropagation(); }; pinMove = (e: PointerEvent) => { console.log('MOVING'); e.stopPropagation(); }; dropPin = (e: DragEvent) => { e.stopPropagation(); e.preventDefault(); document.removeEventListener('drop', this.dropPin, true); document.removeEventListener('pointermove', this.pinMove, true); let target = document.elementFromPoint(e.x, e.y); while (target != null) { if (target === this._ref.current) { const cpt = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY); const x = cpt[0] - this.props.PanelWidth() / 2; const y = cpt[1] - 32 - this.props.PanelHeight() / 2; const location = this._bingMap.current.tryPixelToLocation(new this.MicrosoftMaps.Point(x, y)); this.createPushpin(location.latitude, location.longitude); break; } target = target.parentElement; } }; searchbarKeyDown = (e: any) => { if (e.key === 'Enter') { this.bingSearch(); } }; render() { const renderAnnotations = (childFilters?: () => string[]) => null; return (
e.stopPropagation()} onPointerDown={async e => { e.button === 0 && !e.ctrlKey && e.stopPropagation(); // just a simple test of bing maps geocode api // const loc = await this.bingGeocode(this._bingMap, 'Philadelphia, PA'); // this._bingMap.current.setView({ // mapTypeId: this.MicrosoftMaps.MapTypeId.aerial, // center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude), // zoom: 15, // }); }} style={{ width: `calc(100% - ${this.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
{renderAnnotations(this.transparentFilter)}
{renderAnnotations(this.opaqueFilter)} {SnappingManager.GetIsDragging() ? null : renderAnnotations()} {this.annotationLayer}
typeof newText === 'string' && this.searchbarOnEdit(newText)} onEnter={e => this.bingSearch()} placeholder={this.bingSearchBarContents} textAlign="center" />
(this.draggingPin = false)}>
{!this._mapReady ? null : this.allMapPushpins.map(pushpin => ( new ScriptField(undefined)} onKey={undefined} onDoubleClick={undefined} onBrowseClick={undefined} childFilters={returnEmptyFilter} childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} isDocumentActive={returnFalse} isContentActive={returnFalse} addDocTab={returnFalse} ScreenToLocalTransform={() => new Transform(0, 0, 0)} fitContentsToBox={undefined} focus={returnOne} /> ))}
{/* */} {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : ( )}
{/* */}
{this.sidebarHandle}
); } }