import { GoogleMap, Marker, InfoWindow, InfoBox, useJsApiLoader, LoadScript, GoogleMapProps, StandaloneSearchBox, Autocomplete } from '@react-google-maps/api'; import { observable, action, computed, Lambda, runInAction, IReactionDisposer } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, Field, FieldResult, Opt } from "../../../../fields/Doc"; import { documentSchema } from "../../../../fields/documentSchemas"; import { Id } from "../../../../fields/FieldSymbols"; import { makeInterface } from "../../../../fields/Schema"; import { Cast, NumCast, ScriptCast, StrCast } from "../../../../fields/Types"; import { LinkManager } from "../../../util/LinkManager"; import { undoBatch, UndoManager } from "../../../util/UndoManager"; import "./CollectionMapView.scss"; import { CollectionSubView } from "../CollectionSubView"; import React = require("react"); import requestPromise = require("request-promise"); import ReactDOM from 'react-dom'; import { DragManager } from '../../../util/DragManager'; import { MapMarker } from '../../nodes/MapMarker/MapMarker'; /** * Idea behind storing a marker: * 1. on the map api, have a button "add marker" that adds the marker on the map & store the marker as a node in the collection * (but don't render the marker in the collection itself) * 2. each marker, as a node, has the same feature as all other nodes for linking (the same way one could form a link between a node inside a child collection * and a node outside the child collection) * * /util/LinkManager.ts -- link relations * */ type MapSchema = makeInterface<[typeof documentSchema]>; const MapSchema = makeInterface(documentSchema); export type Coordinates = { lat: number, lng: number, } export type LocationData = { id: string; pos: Coordinates; }; const mapContainerStyle = { height: '100%', }; const defaultCenter = { lat: 38.685, lng: -115.234, }; const mapOptions = { fullscreenControl: false, } const drawingManager = new google.maps.drawing.DrawingManager({ drawingControl: true, drawingControlOptions: { position: google.maps.ControlPosition.TOP_RIGHT, drawingModes: [ google.maps.drawing.OverlayType.MARKER, google.maps.drawing.OverlayType.CIRCLE, google.maps.drawing.OverlayType.POLYLINE, ], }, }); 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 default class CollectionMapView extends CollectionSubView>(MapSchema) { private _dropDisposer?: DragManager.DragDropDisposer; private _disposers: { [name: string]: IReactionDisposer } = {}; @observable private _map = null as unknown as google.maps.Map; @observable private selectedPlace: LocationData | undefined; @observable private markerMap: { [id: string]: google.maps.Marker } = {}; @observable private center = defaultCenter; @observable private zoom = 2.5; @observable private clickedLatLng = null; @observable private infoWindowOpen = false; @observable private bounds = new window.google.maps.LatLngBounds(); @observable private inputRef = React.createRef(); @observable private buttonRef = React.createRef(); @observable private searchMarkers: google.maps.Marker[] = []; @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options); @observable private myPlaces: LocationData[] = [ { id: "id1", pos: { lat: 39.09366509575983, lng: -94.58751660204751 } }, { id: "id2", pos: { lat: 41.82399, lng: -71.41283 } }, { id: "id3", pos: { lat: 47.606214, lng: -122.33207 } }, ]; @action private setSearchBox = (searchBox: any) => { this.searchBox = searchBox; } // iterate myPlaces to size, center, and zoom map to contain all markers private fitBounds = (map: google.maps.Map) => { console.log('map bound is:' + this.bounds); this.myPlaces ? this.myPlaces.map(place => { this.bounds.extend(place.pos!); }) : null; map.fitBounds(this.bounds); } // store a reference to google map instance; fit map bounds to contain all markers @action private loadHandler = (map: google.maps.Map) => { this._map = map; drawingManager.setMap(map); this.fitBounds(map); } @action private markerClickHandler = (e: MouseEvent, place: any) => { // set which place was clicked this.selectedPlace = place; console.log(this.selectedPlace); // used so clicking a second marker works if (this.infoWindowOpen) { this.infoWindowOpen = false; console.log("closeinfowindow") } this.infoWindowOpen = true; console.log("open infowindow") } @action private handleInfoWindowClose = () => { if (this.infoWindowOpen) { this.infoWindowOpen = false; } this.infoWindowOpen = false; this.selectedPlace = undefined; } @action private handlePlaceChanged = () => { console.log(this.searchBox); 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) { console.log(this._map); this._map.fitBounds(place.geometry.viewport); } else { console.log(this._map); 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, }) ) } @action private addMarker = (location: google.maps.LatLng | undefined, map: google.maps.Map) => { new window.google.maps.Marker({ position: location, map: map }); } @action private markerLoadHandler = (marker: google.maps.Marker, place: LocationData) => { place.id ? this.markerMap[place.id] = marker : null; } render() { const { Document, fieldKey, isContentActive: active } = this.props; return
e.stopPropagation()} onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > {/* */}
this.loadHandler(map)} options={mapOptions} > {this.myPlaces ? this.myPlaces.map(place => this.markerLoadHandler(marker, place)} onClick={e => this.markerClickHandler(e, place)} draggable={false} /> ) : null} {this.infoWindowOpen && this.selectedPlace && (





)}
{/*
*/}
; } }