aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/DocumentTypes.ts1
-rw-r--r--src/client/documents/Documents.ts25
-rw-r--r--src/client/views/SidebarAnnos.tsx3
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx2
-rw-r--r--src/client/views/nodes/MapBox/MapBox.scss147
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx554
-rw-r--r--src/client/views/nodes/MapBox/MapBox2.tsx4
-rw-r--r--src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx4
-rw-r--r--src/client/views/nodes/MapBox/MapPushpinBox.tsx38
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx25
-rw-r--r--src/fields/Doc.ts7
11 files changed, 623 insertions, 187 deletions
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index 1d0ddce40..f8d129e79 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -37,6 +37,7 @@ export enum DocumentType {
YOUTUBE = 'youtube',
COMPARISON = 'comparison',
GROUP = 'group',
+ PUSHPIN = "pushpin",
SCRIPTDB = 'scriptdb', // database of scripts
GROUPDB = 'groupdb', // database of groups
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 8a4a82e6d..af4cf2243 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -49,6 +49,7 @@ import { LinkBox } from '../views/nodes/LinkBox';
import { LinkDescriptionPopup } from '../views/nodes/LinkDescriptionPopup';
import { LoadingBox } from '../views/nodes/LoadingBox';
import { MapBox } from '../views/nodes/MapBox/MapBox';
+import { MapPushpinBox } from '../views/nodes/MapBox/MapPushpinBox';
import { PDFBox } from '../views/nodes/PDFBox';
import { PhysicsSimulationBox } from '../views/nodes/PhysicsBox/PhysicsSimulationBox';
import { RecordingBox } from '../views/nodes/RecordingBox/RecordingBox';
@@ -250,6 +251,10 @@ export class DocumentOptions {
recording?: BOOLt = new BoolInfo('whether WebCam is recording or not');
autoPlayAnchors?: BOOLt = new BoolInfo('whether to play audio/video when an anchor is clicked in a stackedTimeline.');
dontPlayLinkOnSelect?: BOOLt = new BoolInfo('whether an audio/video should start playing when a link is followed to it.');
+ dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true);
+ openFactoryLocation?: string; // an OpenWhere value to place the factory created document
+ openFactoryAsDelegate?: boolean; //
+ zoom?: NUMt = new NumInfo('zoom of a mapping view');
updateContentsScript?: ScriptField; // reactive script invoked when viewing a document that can update contents of a collection (or do anything)
toolTip?: string; // tooltip to display on hover
toolType?: string; // type of pen tool
@@ -294,7 +299,11 @@ export class DocumentOptions {
presTransition?: NUMt = new NumInfo('the time taken for the transition TO a document');
presDuration?: NUMt = new NumInfo('the duration of the slide in presentation view');
presZoomText?: BOOLt = new BoolInfo('whether text anchors should shown in a larger box when following links to make them stand out');
-
+
+ presLat?: NUMt = new NumInfo('latitude of a map'); // latitude of a map
+ presLong?: NUMt = new NumInfo('longitude of map'); // longitude of map
+ presZoom?: NUMt = new NumInfo('zoom of map'); // zoom of map
+ presMapType?:string;
data?: any;
data_useCors?: BOOLt = new BoolInfo('whether CORS protocol should be used for web page');
columnHeaders?: List<SchemaHeaderField>; // headers for stacking views
@@ -712,6 +721,13 @@ export namespace Docs {
},
},
],
+ [
+ DocumentType.PUSHPIN,
+ {
+ layout: { view: MapPushpinBox, dataField: defaultDataKey },
+ options: {},
+ },
+ ],
]);
const suffix = 'Proto';
@@ -1053,6 +1069,10 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.MAP), new List(documents), options);
}
+ export function PushpinDocument(lat: number, lng: number, infoWindowOpen: boolean, documents: Array<Doc>, options: DocumentOptions, id?: string) {
+ return InstanceFromProto(Prototypes.get(DocumentType.PUSHPIN), new List(documents), { lat, lng, infoWindowOpen, ...options }, id);
+ }
+
// shouldn't ever need to create a KVP document-- instead set the LayoutTemplateString to be a KeyValueBox for the DocumentView (see addDocTab in TabDocView)
// export function KVPDocument(document: Doc, options: DocumentOptions = {}) {
// return InstanceFromProto(Prototypes.get(DocumentType.KVP), document, { title: document.title + '.kvp', ...options });
@@ -1083,6 +1103,9 @@ export namespace Docs {
id
);
}
+ export function MapanchorDocument(options: DocumentOptions = {}, id?: string) {
+ return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id);
+ }
export function LinearDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _type_collection: CollectionViewType.Linear }, id);
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index db273cc88..cd50865fb 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -228,7 +228,10 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
{Array.from(this.allMetadata.keys())
.sort()
.map(key => renderMeta(key, this.allMetadata.get(key)))}
+ Hello
</div>
+
+
<div style={{ width: '100%', height: `calc(100% - 38px)`, position: 'relative' }}>
<CollectionStackingView
{...this.props}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 2d8663c9c..8ac9d6804 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -47,6 +47,7 @@ import { VideoBox } from './VideoBox';
import { WebBox } from './WebBox';
import React = require('react');
import XRegExp = require('xregexp');
+import { MapPushpinBox } from './MapBox/MapPushpinBox';
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
@@ -271,6 +272,7 @@ export class DocumentContentsView extends React.Component<
PhysicsSimulationBox,
SchemaRowBox,
ImportElementBox,
+ MapPushpinBox,
}}
bindings={bindings}
jsx={layoutFrame}
diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss
index fb15520f6..539c506c7 100644
--- a/src/client/views/nodes/MapBox/MapBox.scss
+++ b/src/client/views/nodes/MapBox/MapBox.scss
@@ -1,87 +1,92 @@
-@import "../../global/globalCssVariables.scss";
+@import '../../global/globalCssVariables.scss';
.mapBox {
- width: 100%;
- height: 100%;
- overflow: hidden;
- display: flex;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ display: flex;
- .mapBox-infoWindow {
- background-color: white;
- opacity: 0.75;
- padding: 12;
- font-size: 17;
- }
+ .mapBox-infoWindow {
+ background-color: white;
+ opacity: 0.75;
+ padding: 12;
+ font-size: 17;
+ }
- .mapBox-overlayButton-sidebar {
- background: #121721;
- height: 25px;
- width: 25px;
- right: 5px;
- display: flex;
- position: absolute;
- align-items: center;
- justify-content: center;
- border-radius: 3px;
- pointer-events: all;
- z-index: 1; // so it appears on top of the document's title, if shown
-
- box-shadow: $standard-box-shadow;
- transition: 0.2s;
-
- &:hover{
- filter: brightness(0.85);
- }
- }
+ .mapBox-overlayButton-sidebar {
+ background: #121721;
+ height: 25px;
+ width: 25px;
+ right: 5px;
+ display: flex;
+ position: absolute;
+ align-items: center;
+ justify-content: center;
+ border-radius: 3px;
+ pointer-events: all;
+ z-index: 1; // so it appears on top of the document's title, if shown
- .mapBox-wrapper {
- width: 100%;
- .mapBox-input {
- box-sizing: border-box;
- border: 1px solid transparent;
- width: 240px;
- height: 32px;
- padding: 0 12px;
- border-radius: 3px;
- box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
- font-size: 14px;
- outline: none;
- text-overflow: ellipses;
- position: absolute;
- left: 50%;
- margin-left: -120px;
- }
- }
+ box-shadow: $standard-box-shadow;
+ transition: 0.2s;
- .mapBox-sidebar-handle {
- top: 0;
- //top: calc(50% - 17.5px); // use this to center vertically -- make sure it looks okay for slide views
- width: 10px;
- height: 100%;
- max-height: 35px;
- background: lightgray;
- border-radius: 20px;
- cursor:grabbing;
+ &:hover {
+ filter: brightness(0.85);
}
- .mapBox-addMarker {
+ }
+
+ .mapBox-wrapper {
+ width: 100%;
+ .mapBox-input {
+ box-sizing: border-box;
+ border: 1px solid transparent;
+ width: 240px;
+ height: 32px;
+ padding: 0 12px;
+ border-radius: 3px;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
+ font-size: 14px;
+ outline: none;
+ text-overflow: ellipses;
+ position: absolute;
left: 50%;
- margin-left: 120px;
- right: unset !important;
- margin-top: -10;
- height: max-content;
- }
- .searchbox {
- display:none;
- }
- .mapBox-addMarker {
- display:none;
+ margin-left: -120px;
}
+ }
+ .mapBox-sidebar {
+ position: absolute;
+ right: 0;
+ height: 100%;
+ }
+
+ .mapBox-sidebar-handle {
+ top: 0;
+ //top: calc(50% - 17.5px); // use this to center vertically -- make sure it looks okay for slide views
+ width: 10px;
+ height: 100%;
+ max-height: 35px;
+ background: lightgray;
+ border-radius: 20px;
+ cursor: grabbing;
+ }
+ .mapBox-addMarker {
+ left: 50%;
+ margin-left: 120px;
+ right: unset !important;
+ margin-top: -10;
+ height: max-content;
+ }
+ .searchbox {
+ display: none;
+ }
+ .mapBox-addMarker {
+ display: none;
+ }
}
.mapBox:hover {
.mapBox-addMarker {
- display:block;
+ display: block;
}
.searchbox {
- display :block;
+ display: block;
}
}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 4919ee94c..15cf22953 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,18 +1,23 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
+import { GoogleMapProps, Marker } from '@react-google-maps/api';
import BingMapsReact from 'bingmaps-react';
-import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
+import { EditableText } from 'browndash-components';
+import e from 'connect-flash';
+import { truncateSync } from 'fs';
+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, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, returnAll, returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, 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 { UndoManager } from '../../../util/UndoManager';
import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
@@ -21,11 +26,12 @@ import { MarqueeAnnotator } from '../../MarqueeAnnotator';
import { AnchorMenu } from '../../pdf/AnchorMenu';
import { Annotation } from '../../pdf/Annotation';
import { SidebarAnnos } from '../../SidebarAnnos';
+import { DocumentView } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
-import { PinProps } from '../trails';
+import { PinProps, PresBox } from '../trails';
import './MapBox.scss';
import { MapBoxInfoWindow } from './MapBoxInfoWindow';
-
+// amongus
/**
* MapBox architecture:
* Main component: MapBox.tsx
@@ -55,7 +61,6 @@ const mapOptions = {
};
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=<your apikey>
-const apiKey = process.env.GOOGLE_MAPS;
const script = document.createElement('script');
script.defer = true;
@@ -88,7 +93,6 @@ const options = {
@observer
export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
- static UseBing = true;
private _dropDisposer?: DragManager.DragDropDisposer;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
@@ -110,10 +114,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@observable private markerMap: { [id: string]: google.maps.Marker } = {};
@observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter;
@observable private _marqueeing: number[] | undefined;
- @observable private _isAnnotating = false;
@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 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]);
@@ -133,16 +136,54 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
static _hadSelection: boolean = false;
private _sidebarRef = React.createRef<SidebarAnnos>();
private _ref: React.RefObject<HTMLDivElement> = React.createRef();
-
+ private _disposer: {[key:string]:IReactionDisposer} = {}
componentDidMount() {
this.props.setContentView?.(this);
+ this._disposer.location = reaction(() => ({lat:this.rootDoc.latitude, lng:this.rootDoc.longitude, zoom:this.rootDoc.zoom,mapType:this.rootDoc.mapType}),
+ (locationObject) => {
+
+ this._bingMap.current.setView({
+ mapTypeId: locationObject.mapType,
+ zoom:locationObject.zoom,
+ center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng),
+ });
+
+ }, {fireImmediately: true});
+ // this._disposer.pins = reaction(() => ({pinList:this.dataDoc[this.annotationKey]}),
+ // (pinsObject) => {
+ // this._bingMap.current.entities.clear();
+ // pinsObject.pinList.map((pushpin: Doc) => (
+ // <DocumentView
+ // {...this.props}
+ // Document={pushpin}
+ // DataDoc={undefined}
+ // PanelWidth={returnOne}
+ // PanelHeight={returnOne}
+ // NativeWidth={returnOne}
+ // NativeHeight={returnOne}
+ // onClick={()=>new ScriptField(undefined)}
+ // onKey={undefined}
+ // onDoubleClick={undefined}
+ // onBrowseClick={undefined}
+ // docFilters={returnEmptyDoclist}
+ // docRangeFilters={returnEmptyDoclist}
+ // searchFilterDocs={returnEmptyDoclist}
+ // isDocumentActive={returnFalse}
+ // isContentActive={returnFalse}
+ // addDocTab={returnFalse}
+ // ScreenToLocalTransform={()=>new Transform(0,0,0)}
+ // fitContentsToBox={undefined}
+ // focus={returnOne}
+ // />));
+
+ // }, {fireImmediately: false});
+
+
}
- @action
- private setSearchBox = (searchBox: any) => {
- this.searchBox = searchBox;
- };
-
+ 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();
@@ -218,6 +259,27 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
controlDiv.appendChild(controlUI);
return controlDiv;
};
+ /**
+ * 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
@@ -231,7 +293,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
map: map,
});
map.panTo(position);
- const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
+ const mapMarker = Docs.Create.PushpinDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
this.addDocument(mapMarker, this.annotationKey);
};
@@ -298,25 +360,49 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
};
/**
- * Load and render all map markers
- * @param marker
- * @param place
+ * 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 markerLoadHandler = (marker: google.maps.Marker, place: Doc) => {
- place[Id] ? (this.markerMap[place[Id]] = marker) : null;
- };
+ private handlePlaceChanged = () => {
+ const place = this.searchBox.getPlace();
- /**
- * 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;
+ 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,
+ })
+ );
};
/**
@@ -335,7 +421,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (existingMarker) {
Doc.AddDocToList(existingMarker, 'data', doc);
} else {
- const marker = Docs.Create.MapMarkerDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
+ const marker = Docs.Create.PushpinDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
this.addDocument(marker, this.annotationKey);
}
}
@@ -397,52 +483,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
/**
- * 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() {
@@ -503,7 +543,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
};
@action finishMarquee = (x?: number, y?: number) => {
this._marqueeing = undefined;
- this._isAnnotating = false;
x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false);
};
@@ -525,7 +564,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
);
}
- getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc;
+ // 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
@@ -590,14 +630,285 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
});
};
+ /**
+ *
+ *
+ * ERIC'S BING MAP CODE BELOW
+ *
+ *
+ *
+ **/
+
+ bingSearchBarContents: any = 'Boston, MA'; // 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 = (pinDoc:Doc,pin:any) => {
+ this.removePushpin(pinDoc,pin);
+ };
+ /*
+ * Pushpin onclick
+ */
+ @action
+ pushpinClicked = (pinDoc:Doc,pin:any) => {
+ // TODO:
+ // if (sidebarannos is not open) open sidebarannos
+ // creates button onclick removes the doc from annotations
+
+ // pan to pushpin location
+ this.dataDoc.latitude = pinDoc.lat;
+ this.dataDoc.longitude = pinDoc.lng;
+
+
+
+
+ // @action
+ // onPointerDown = (e: React.PointerEvent) => {
+ // if (e.button === 2 || e.ctrlKey) {
+ // AnchorMenu.Instance.Status = 'annotation';
+ // AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this);
+ // AnchorMenu.Instance.Pinned = false;
+ // AnchorMenu.Instance.PinToPres = this.pinToPres;
+ // AnchorMenu.Instance.MakeTargetToggle = this.makeTargretToggle;
+ // AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler;
+ // AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this.annoTextRegion);
+ // AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
+ // e.stopPropagation();
+ // } else if (e.button === 0) {
+ // e.stopPropagation();
+ // LinkFollower.FollowLink(undefined, this.annoTextRegion, false);
+ // }
+ // };
+
+ // TODO: UPDATE FOR DASHDOC SELECTION
+ };
+
+ /**
+ * 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.zoom = 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.zoom = 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',
+ });
+ 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) => {
+ const anchor =
+ Docs.Create.MapanchorDocument({
+ title: 'MapAnchor:' + this.rootDoc.title,
+ presLat: NumCast(this.dataDoc.latitude),
+ presLong: NumCast(this.dataDoc.longitude),
+ presZoom: NumCast(this.dataDoc.zoom),
+ presMapType: this.dataDoc.mapType,
+ // preslocationToLookAt:this.dataDoc.locationToLookAt,
+ // presType:
+ 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.lat, pin.lng), {}): new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.lat, pin.lng),
+ // {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,pushPin));
+ this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pin,pushPin));
+ }
+
+
+
+ @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: defaultCenter.lat, longitude: defaultCenter.lng },
+ 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',
+ navigationBarMode: 'compact',
+ 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: boolean | undefined;
+ @action
+ togglePlacePin = () => {
+ if (this.placePinOn) this.placePinOn = false;
+ else this.placePinOn = true;
};
- bingMapReady = (map: any) => (this._bingMap = map.map);
+
+ /*
+ * Called when BingMap is first rendered
+ * Initializes starting values
+ */
+ bingMapReady = (map: any) => {
+ this._bingMap = map.map;
+ this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick);
+ this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', this.updateLayout);
+ this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', this.updateMapType);
+ this.updateLayout();
+ this.updateMapType();
+ }
+
render() {
const renderAnnotations = (childFilters?: () => string[]) => null;
return (
@@ -621,33 +932,54 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
{SnappingManager.GetIsDragging() ? null : renderAnnotations()}
{this.annotationLayer}
- {!MapBox.UseBing ? null : <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions} />}
- <div style={{ display: MapBox.UseBing ? 'none' : undefined }}>
- <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
- <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
- <input className="mapBox-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
- </Autocomplete>
-
- {this.renderMarkers()}
- {this.allMapMarkers
- .filter(marker => marker.infoWindowOpen)
- .map(marker => (
- <MapBoxInfoWindow
- key={marker[Id]}
- {...this.props}
- setContentView={emptyFunction}
- place={marker}
- markerMap={this.markerMap}
- PanelWidth={this.infoWidth}
- PanelHeight={this.infoHeight}
- moveDocument={this.moveDocument}
- isAnyChildContentActive={this.isAnyChildContentActive}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- />
- ))}
- {/* {this.handleMapCenter(this._map)} */}
- </GoogleMap>
+ <EditableText setEditing={(isEditing: boolean) => ({})} editing onEdit={(newText: string) => (this.bingSearchBarContents = newText)} placeholder="..." text="Boston, MA" />
+
+ <input type="button" value="Search" onClick={this.bingSearch} />
+
+ {this.placePinOn ? <input type="button" value="Place Pin Mode On" onClick={this.togglePlacePin} /> : <input type="button" value="Place Pin Mode Off" onClick={this.togglePlacePin} />}
+ {/* {this.pinIsSelected_TEMPORARY? <input type="button" value="Delete Pin" onClick={this.removeSelectedPin} /> : null} */}
+ <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions}>
+ </BingMapsReact>
+ <div>
+
+ {this.allMapPushpins
+ .map(pushpin => (
+ <DocumentView
+ {...this.props}
+ Document={pushpin}
+ DataDoc={undefined}
+ PanelWidth={returnOne}
+ PanelHeight={returnOne}
+ NativeWidth={returnOne}
+ NativeHeight={returnOne}
+ onClick={()=>new ScriptField(undefined)}
+ onKey={undefined}
+ onDoubleClick={undefined}
+ onBrowseClick={undefined}
+ docFilters={returnEmptyDoclist}
+ docRangeFilters={returnEmptyDoclist}
+ searchFilterDocs={returnEmptyDoclist}
+ isDocumentActive={returnFalse}
+ isContentActive={returnFalse}
+ addDocTab={returnFalse}
+ ScreenToLocalTransform={()=>new Transform(0,0,0)}
+ fitContentsToBox={undefined}
+ focus={returnOne}
+ />
+ ))}
</div>
+ {/* <MapBoxInfoWindow
+ key={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})[Id]}
+ {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+ place={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})}
+ markerMap={this.markerMap}
+ PanelWidth={this.infoWidth}
+ PanelHeight={this.infoHeight}
+ moveDocument={this.moveDocument}
+ isAnyChildContentActive={this.isAnyChildContentActive}
+ whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+ /> */}
+
{!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
<MarqueeAnnotator
rootDoc={this.rootDoc}
@@ -666,7 +998,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
)}
</div>
{/* </LoadScript > */}
- <div className="mapBox-sidebar" style={{ position: 'absolute', right: 0, height: '100%', width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+ <div className="mapBox-sidebar" style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
<SidebarAnnos
ref={this._sidebarRef}
{...this.props}
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index a9154c5bb..643aeaee0 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -229,7 +229,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
map: map,
});
map.panTo(position);
- const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
+ const mapMarker = Docs.Create.PushpinDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
this.addDocument(mapMarker, this.annotationKey);
};
@@ -333,7 +333,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (existingMarker) {
Doc.AddDocToList(existingMarker, 'data', doc);
} else {
- const marker = Docs.Create.MapMarkerDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
+ const marker = Docs.Create.PushpinDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
this.addDocument(marker, this.annotationKey);
}
}
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index 577101445..66c47d131 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -47,7 +47,9 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, 'data', d), true as boolean);
render() {
return (
- <InfoWindow anchor={this.props.markerMap[this.props.place[Id]]} onCloseClick={this.handleInfoWindowClose}>
+ <InfoWindow
+ // anchor={this.props.markerMap[this.props.place[Id]]}
+ onCloseClick={this.handleInfoWindowClose}>
<div className="mapbox-infowindow">
<div style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}>
<CollectionStackingView
diff --git a/src/client/views/nodes/MapBox/MapPushpinBox.tsx b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
new file mode 100644
index 000000000..588bddd1a
--- /dev/null
+++ b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
@@ -0,0 +1,38 @@
+import { observer } from 'mobx-react';
+// import { SettingsManager } from '../../../util/SettingsManager';
+import { ViewBoxBaseComponent } from '../../DocComponent';
+import { FieldView, FieldViewProps } from '../FieldView';
+import React = require('react');
+import { computed } from 'mobx';
+import { MapBox } from './MapBox';
+
+
+/**
+ * Map Pushpin doc class
+ */
+@observer
+export class MapPushpinBox extends ViewBoxBaseComponent<FieldViewProps>() {
+
+
+ public static LayoutString(fieldKey: string) {
+ return FieldView.LayoutString(MapPushpinBox, fieldKey);
+ }
+ componentDidMount() {
+ this.mapBoxView.addPushpin(this.rootDoc);
+ }
+ componentWillUnmount() {
+ // this.mapBoxView.removePushpin(this.rootDoc);
+ }
+
+
+ @computed get mapBoxView() {
+ return this.props.DocumentView?.()?.props.docViewPath().lastElement()?.ComponentView as MapBox;
+ }
+ @computed get mapBox() {
+ return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc;
+ }
+
+ render() {
+ return (<div></div>);
+ }
+}
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 56af67802..c0cd3ab70 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -383,6 +383,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const inkable = [DocumentType.INK].includes(targetType);
const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetType) || target?._type_collection === CollectionViewType.Stacking;
const pannable = [DocumentType.IMG, DocumentType.PDF].includes(targetType) || (targetType === DocumentType.COL && target?._type_collection === CollectionViewType.Freeform);
+ const map = [DocumentType.MAP].includes(targetType);
const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(targetType);
const clippable = [DocumentType.COMPARISON].includes(targetType);
const datarange = [DocumentType.FUNCPLOT].includes(targetType);
@@ -392,7 +393,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const filters = true;
const pivot = true;
const dataannos = false;
- return { scrollable, pannable, inkable, type_collection, pivot, filters, temporal, clippable, dataview, datarange, poslayoutview, dataannos };
+ return { scrollable, pannable, inkable, type_collection, pivot, map, filters, temporal, clippable, dataview, datarange, poslayoutview, dataannos };
}
@action
@@ -460,6 +461,24 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
changed = true;
}
}
+ if (pinDataTypes?.map || (!pinDataTypes && activeItem.presLat !== undefined)) {
+ if (bestTarget.latitude !== activeItem.presLat) {
+ Doc.SetInPlace(bestTarget, "latitude", NumCast(activeItem.presLat), true);
+ changed = true;
+ }
+ if (bestTarget.longitude !== activeItem.presLong) {
+ Doc.SetInPlace(bestTarget, "longitude", NumCast(activeItem.presLong), true);
+ changed = true;
+ }
+ if (bestTarget.zoom !== activeItem.presZoom) {
+ Doc.SetInPlace(bestTarget, "zoom", NumCast(activeItem.presZoom), true);
+ changed = true;
+ }
+ if (bestTarget.mapType !== activeItem.presMapType) {
+ Doc.SetInPlace(bestTarget, "mapType", StrCast(activeItem.presMapType), true);
+ changed = true;
+ }
+ }
if (pinDataTypes?.temporal || (!pinDataTypes && activeItem.presStartTime !== undefined)) {
if (bestTarget._layout_currentTimecode !== activeItem.presStartTime) {
bestTarget._layout_currentTimecode = activeItem.presStartTime;
@@ -638,6 +657,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
pinDoc.presXRange = undefined; //targetDoc?.xrange;
pinDoc.presYRange = undefined; //targetDoc?.yrange;
}
+ if (pinProps.pinData.map) {
+ pinDoc.presLat = targetDoc?.lat;
+ //...
+ }
if (pinProps.pinData.poslayoutview)
pinDoc.presPinLayoutData = new List<string>(
DocListCast(targetDoc[fkey] as ObjectField).map(d =>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index eb52cff88..4ed7ccb61 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -510,6 +510,13 @@ export namespace Doc {
export function IsDelegateField(doc: Doc, fieldKey: string) {
return doc && Get(doc, fieldKey, true) !== undefined;
}
+ //
+ // this will write the value to the key on either the data doc or the embedding doc. The choice
+ // of where to write it is based on:
+ // 1) if the embedding Doc already has this field defined on it, then it will be written to the embedding
+ // 2) if the data doc has the field, then it's written there.
+ // 3) if neither already has the field, then 'defaultProto' determines whether to write it to the data doc (or the embedding)
+ //
export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) {
if (key.startsWith('_')) key = key.substring(1);
const hasProto = Doc.GetProto(doc) !== doc ? Doc.GetProto(doc) : undefined;