Added ID's and update times for more accurate map merging during updates.
This commit is contained in:
parent
088777d742
commit
eceb85569e
@ -44,6 +44,7 @@
|
|||||||
"ng-click-outside": "^5.0.0",
|
"ng-click-outside": "^5.0.0",
|
||||||
"ngx-color-picker": "^8.1.0",
|
"ngx-color-picker": "^8.1.0",
|
||||||
"rxjs": "~6.4.0",
|
"rxjs": "~6.4.0",
|
||||||
|
"ts-md5": "^1.2.5",
|
||||||
"tslib": "^1.9.0",
|
"tslib": "^1.9.0",
|
||||||
"web-animations-js": "^2.3.2",
|
"web-animations-js": "^2.3.2",
|
||||||
"whatwg-fetch": "^3.0.0",
|
"whatwg-fetch": "^3.0.0",
|
||||||
|
@ -15,26 +15,24 @@ import {PermissionsComponent} from "./components/permissions/permissions.compone
|
|||||||
import {AngularFireModule} from "@angular/fire";
|
import {AngularFireModule} from "@angular/fire";
|
||||||
import {AngularFirestoreModule} from "@angular/fire/firestore";
|
import {AngularFirestoreModule} from "@angular/fire/firestore";
|
||||||
import {ToolbarComponent} from "./components/toolbar/toolbar.component";
|
import {ToolbarComponent} from "./components/toolbar/toolbar.component";
|
||||||
import {PaletteComponent} from "./components/palette/palette.component";
|
import {PaletteComponent} from "./components/palette/palette.component"
|
||||||
import {MarkerComponent} from "./components/marker/marker.component";
|
|
||||||
import {AnimatedBackgroundComponent} from "./components/animatedBackground/animatedBackground.component";
|
import {AnimatedBackgroundComponent} from "./components/animatedBackground/animatedBackground.component";
|
||||||
import {ClickOutsideModule} from "ng-click-outside";
|
import {ClickOutsideModule} from "ng-click-outside";
|
||||||
import {CircleComponent} from "./components/circle/circle.component";
|
|
||||||
import {ColorPickerModule} from "ngx-color-picker";
|
import {ColorPickerModule} from "ngx-color-picker";
|
||||||
import {ColorPickerDialogComponent} from "./components/colorPickerDialog/colorPickerDialog.component";
|
import {ColorPickerDialogComponent} from "./components/colorPickerDialog/colorPickerDialog.component";
|
||||||
import {DimensionsDialogComponent} from "./components/dimensionsDialog/dimensionsDialog.component";
|
import {DimensionsDialogComponent} from "./components/dimensionsDialog/dimensionsDialog.component";
|
||||||
|
import {EditSymbolComponent} from "./components/editSymbol/editSymbol.component";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AnimatedBackgroundComponent,
|
AnimatedBackgroundComponent,
|
||||||
AppComponent,
|
AppComponent,
|
||||||
CalibrateComponent,
|
CalibrateComponent,
|
||||||
CircleComponent,
|
|
||||||
ColorPickerDialogComponent,
|
ColorPickerDialogComponent,
|
||||||
DimensionsDialogComponent,
|
DimensionsDialogComponent,
|
||||||
|
EditSymbolComponent,
|
||||||
HomeComponent,
|
HomeComponent,
|
||||||
MapComponent,
|
MapComponent,
|
||||||
MarkerComponent,
|
|
||||||
PaletteComponent,
|
PaletteComponent,
|
||||||
PermissionsComponent,
|
PermissionsComponent,
|
||||||
ToolbarComponent
|
ToolbarComponent
|
||||||
@ -53,7 +51,7 @@ import {DimensionsDialogComponent} from "./components/dimensionsDialog/dimension
|
|||||||
MatInputModule,
|
MatInputModule,
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
entryComponents: [CalibrateComponent, CircleComponent, ColorPickerDialogComponent, DimensionsDialogComponent, MarkerComponent, PermissionsComponent],
|
entryComponents: [CalibrateComponent, ColorPickerDialogComponent, DimensionsDialogComponent, EditSymbolComponent, PermissionsComponent],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
export class AppModule {
|
export class AppModule {
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
z-index: 600;
|
z-index: 6000;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
import {Component, Inject} from "@angular/core";
|
|
||||||
import {MatBottomSheetRef} from "@angular/material";
|
|
||||||
import {MAT_BOTTOM_SHEET_DATA} from "@angular/material/bottom-sheet";
|
|
||||||
import {Circle} from "../../models/mapSymbol";
|
|
||||||
import {MatDialog} from "@angular/material/dialog";
|
|
||||||
import {ColorPickerDialogComponent} from "../colorPickerDialog/colorPickerDialog.component";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'calibrate',
|
|
||||||
templateUrl: 'circle.component.html'
|
|
||||||
})
|
|
||||||
export class CircleComponent {
|
|
||||||
symbol: any;
|
|
||||||
circle: Circle;
|
|
||||||
|
|
||||||
constructor(private dialog: MatDialog, private bottomSheetRef: MatBottomSheetRef, @Inject(MAT_BOTTOM_SHEET_DATA) circle) {
|
|
||||||
this.symbol = circle;
|
|
||||||
this.circle = this.symbol.symbol;
|
|
||||||
|
|
||||||
this.symbol.enableEdit();
|
|
||||||
|
|
||||||
this.bottomSheetRef.afterDismissed().subscribe(() => this.symbol.disableEdit());
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
this.circle.latlng = this.symbol.getLatLng();
|
|
||||||
this.circle.radius = this.symbol.getRadius();
|
|
||||||
this.bottomSheetRef.dismiss(this.symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
colorPicker() {
|
|
||||||
this.dialog.open(ColorPickerDialogComponent, {data: this.circle.color, hasBackdrop: false, panelClass: 'p-0'}).afterClosed()
|
|
||||||
.subscribe(color => {
|
|
||||||
this.circle.color = color
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,14 +2,14 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<mat-form-field appearance="fill" class="col-12 col-md-6">
|
<mat-form-field appearance="fill" class="col-12 col-md-6">
|
||||||
<mat-label>Label</mat-label>
|
<mat-label>Label</mat-label>
|
||||||
<input matInput [(ngModel)]="circle.label">
|
<input matInput [(ngModel)]="symbol.label">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<div class="ml-auto mt-1 mr-3 rounded-circle border" (click)="colorPicker()" style="width: 46px; height: 46px;" [style.background]="circle.color"></div>
|
<div class="ml-auto mt-1 mr-3 rounded-circle border curs-pointer" (click)="colorPicker()" style="width: 46px; height: 46px;" [style.background]="symbol.color"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<mat-form-field appearance="fill" class="col-12">
|
<mat-form-field appearance="fill" class="col-12">
|
||||||
<mat-label>Notes</mat-label>
|
<mat-label>Notes</mat-label>
|
||||||
<textarea matInput rows="3"></textarea>
|
<textarea matInput rows="3" [(ngModel)]="symbol.notes"></textarea>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
36
src/app/components/editSymbol/editSymbol.component.ts
Normal file
36
src/app/components/editSymbol/editSymbol.component.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import {Component, Inject} from "@angular/core";
|
||||||
|
import {MatDialog} from "@angular/material/dialog";
|
||||||
|
import {MapSymbol} from "../../models/mapSymbol";
|
||||||
|
import {MAT_BOTTOM_SHEET_DATA, MatBottomSheetRef} from "@angular/material/bottom-sheet";
|
||||||
|
import {ColorPickerDialogComponent} from "../colorPickerDialog/colorPickerDialog.component";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'edit-symbol',
|
||||||
|
templateUrl: 'editSymbol.component.html'
|
||||||
|
})
|
||||||
|
export class EditSymbolComponent {
|
||||||
|
symbol: MapSymbol;
|
||||||
|
mapItem;
|
||||||
|
|
||||||
|
constructor(private dialog: MatDialog, private ref: MatBottomSheetRef, @Inject(MAT_BOTTOM_SHEET_DATA) data) {
|
||||||
|
this.symbol = Object.assign({color: '#ff4141'}, data.symbol);
|
||||||
|
this.mapItem = data.item;
|
||||||
|
|
||||||
|
this.mapItem.enableEdit();
|
||||||
|
this.ref.afterDismissed().subscribe(() => this.mapItem.disableEdit());
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
let latlng = this.mapItem.getLatLng();
|
||||||
|
this.symbol.latlng = {lat: latlng.lat, lng: latlng.lng};
|
||||||
|
if(this.mapItem.getRadius) this.symbol['radius'] = this.mapItem.getRadius();
|
||||||
|
this.ref.dismiss(this.symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
colorPicker() {
|
||||||
|
this.dialog.open(ColorPickerDialogComponent, {data: this.symbol.color, hasBackdrop: false, panelClass: 'p-0'}).afterClosed()
|
||||||
|
.subscribe(color => {
|
||||||
|
this.symbol.color = color
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +0,0 @@
|
|||||||
<div>
|
|
||||||
<div class="row">
|
|
||||||
<mat-form-field appearance="fill" class="col-12 col-md-6">
|
|
||||||
<mat-label>Label</mat-label>
|
|
||||||
<input matInput [(ngModel)]="marker.label">
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
<mat-divider></mat-divider>
|
|
||||||
<button mat-button class="float-right mt-2" (click)="close()">Close</button>
|
|
||||||
</div>
|
|
@ -1,16 +0,0 @@
|
|||||||
import {Component, Inject} from "@angular/core";
|
|
||||||
import {MatBottomSheetRef} from "@angular/material";
|
|
||||||
import {MAT_BOTTOM_SHEET_DATA} from "@angular/material/bottom-sheet";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'calibrate',
|
|
||||||
templateUrl: 'marker.component.html'
|
|
||||||
})
|
|
||||||
export class MarkerComponent {
|
|
||||||
|
|
||||||
constructor(private bottomSheetRef: MatBottomSheetRef, @Inject(MAT_BOTTOM_SHEET_DATA) public marker) { }
|
|
||||||
|
|
||||||
close() {
|
|
||||||
this.bottomSheetRef.dismiss();
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,19 +4,21 @@ export interface LatLng {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface MapData {
|
export interface MapData {
|
||||||
circles?: Circle[];
|
circles?: {[key: string]: Circle};
|
||||||
locations?: {[key: string]: Marker};
|
locations?: {[key: string]: Marker};
|
||||||
markers?: Marker[];
|
markers?: {[key: string]: Marker};
|
||||||
measurements?: Measurement[];
|
measurements?: {[key: string]: Measurement};
|
||||||
polygons?: Polygon[];
|
polygons?: {[key: string]: Polygon};
|
||||||
polylines?: Polyline[];
|
polylines?: {[key: string]: Polyline};
|
||||||
rectangles?: Rectangle[];
|
rectangles?: {[key: string]: Rectangle};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MapSymbol {
|
export interface MapSymbol {
|
||||||
|
deleted?: boolean;
|
||||||
|
id?: string;
|
||||||
symbol?: any;
|
symbol?: any;
|
||||||
latlng?: LatLng | LatLng[];
|
latlng?: LatLng | LatLng[];
|
||||||
new?: boolean;
|
noClick?: boolean;
|
||||||
noDelete?: boolean;
|
noDelete?: boolean;
|
||||||
noDeleteTool?: boolean;
|
noDeleteTool?: boolean;
|
||||||
noSelect?: boolean;
|
noSelect?: boolean;
|
||||||
@ -26,6 +28,7 @@ export interface MapSymbol {
|
|||||||
interactive?: boolean;
|
interactive?: boolean;
|
||||||
rotationAngle?: number;
|
rotationAngle?: number;
|
||||||
rotationOrigin?: string;
|
rotationOrigin?: string;
|
||||||
|
updated?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Circle extends MapSymbol {
|
export interface Circle extends MapSymbol {
|
||||||
|
@ -108,7 +108,7 @@ export class MapService {
|
|||||||
newCircle(c: Circle) {
|
newCircle(c: Circle) {
|
||||||
let circle = L.circle(c.latlng, Object.assign({color: '#ff4141'}, c)).addTo(this.map);
|
let circle = L.circle(c.latlng, Object.assign({color: '#ff4141'}, c)).addTo(this.map);
|
||||||
if(c.label) circle.bindTooltip(c.label, {permanent: true, direction: 'center'});
|
if(c.label) circle.bindTooltip(c.label, {permanent: true, direction: 'center'});
|
||||||
circle.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: c, item: circle}));
|
if(!c.noClick) circle.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: c, item: circle}));
|
||||||
if(!c.noDelete) this.circles.push(circle);
|
if(!c.noDelete) this.circles.push(circle);
|
||||||
return circle;
|
return circle;
|
||||||
}
|
}
|
||||||
@ -116,7 +116,7 @@ export class MapService {
|
|||||||
newMarker(m: Marker) {
|
newMarker(m: Marker) {
|
||||||
let marker = L.marker(m.latlng, Object.assign({color: '#ff4141'}, m, {icon: m.icon ? this.getIcon(m.icon) : MARKER})).addTo(this.map);
|
let marker = L.marker(m.latlng, Object.assign({color: '#ff4141'}, m, {icon: m.icon ? this.getIcon(m.icon) : MARKER})).addTo(this.map);
|
||||||
if(m.label) marker.bindTooltip(m.label, {permanent: true, direction: 'bottom'});
|
if(m.label) marker.bindTooltip(m.label, {permanent: true, direction: 'bottom'});
|
||||||
marker.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: m, item: marker}));
|
if(!m.noClick) marker.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: m, item: marker}));
|
||||||
if(!m.noDelete) this.markers.push(marker);
|
if(!m.noDelete) this.markers.push(marker);
|
||||||
return marker;
|
return marker;
|
||||||
}
|
}
|
||||||
@ -132,20 +132,20 @@ export class MapService {
|
|||||||
|
|
||||||
let distance = latLngDistance(m.latlng, m.latlng2);
|
let distance = latLngDistance(m.latlng, m.latlng2);
|
||||||
line.bindPopup(`${distance > 1000 ? Math.round(distance / 100) / 10 : Math.round(distance)} ${distance > 1000 ? 'k' : ''}m`, {autoClose: false, closeOnClick: false}).openPopup();
|
line.bindPopup(`${distance > 1000 ? Math.round(distance / 100) / 10 : Math.round(distance)} ${distance > 1000 ? 'k' : ''}m`, {autoClose: false, closeOnClick: false}).openPopup();
|
||||||
line.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: m, item: group}));
|
if(m.noClick) line.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: m, item: group}));
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
newPolygon(p: Polygon) {
|
newPolygon(p: Polygon) {
|
||||||
let polygon = new L.Polygon(p.latlng, Object.assign({color: '#ff4141'}, p)).addTo(this.map);
|
let polygon = new L.Polygon(p.latlng, Object.assign({color: '#ff4141'}, p)).addTo(this.map);
|
||||||
polygon.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: p, item: polygon}));
|
if(!p.noClick) polygon.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: p, item: polygon}));
|
||||||
if(!p.noDelete) this.polygons.push(polygon);
|
if(!p.noDelete) this.polygons.push(polygon);
|
||||||
return polygon;
|
return polygon;
|
||||||
}
|
}
|
||||||
|
|
||||||
newPolyline(p: Polyline) {
|
newPolyline(p: Polyline) {
|
||||||
let polyline = new L.Polyline(p.latlng, Object.assign({color: '#ff4141', weight: 10}, p)).addTo(this.map);
|
let polyline = new L.Polyline(p.latlng, Object.assign({color: '#ff4141', weight: 10}, p)).addTo(this.map);
|
||||||
polyline.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: p, item: polyline}));
|
if(!p.noClick) polyline.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: p, item: polyline}));
|
||||||
if(!p.noDelete) this.polylines.push(polyline);
|
if(!p.noDelete) this.polylines.push(polyline);
|
||||||
return polyline;
|
return polyline;
|
||||||
}
|
}
|
||||||
@ -153,7 +153,7 @@ export class MapService {
|
|||||||
newRectangle(r: Rectangle) {
|
newRectangle(r: Rectangle) {
|
||||||
let rect = new L.Rectangle([r.latlng, r.latlng2], Object.assign({color: '#ff4141'}, r)).addTo(this.map);
|
let rect = new L.Rectangle([r.latlng, r.latlng2], Object.assign({color: '#ff4141'}, r)).addTo(this.map);
|
||||||
if(r.label) rect.bindTooltip(r.label, {permanent: true, direction: 'center'});
|
if(r.label) rect.bindTooltip(r.label, {permanent: true, direction: 'center'});
|
||||||
rect.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: r, item: rect}));
|
if(!r.noClick) rect.on('click', e => this.click.next({latlng: {lat: e.latlng.lat, lng: e.latlng.lng}, symbol: r, item: rect}));
|
||||||
if(!r.noDelete) this.rectangles.push(rect);
|
if(!r.noDelete) this.rectangles.push(rect);
|
||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import {Injectable} from "@angular/core";
|
|||||||
import {AngularFirestore, AngularFirestoreDocument} from "@angular/fire/firestore";
|
import {AngularFirestore, AngularFirestoreDocument} from "@angular/fire/firestore";
|
||||||
import {BehaviorSubject, combineLatest, Subscription} from "rxjs";
|
import {BehaviorSubject, combineLatest, Subscription} from "rxjs";
|
||||||
import {Circle, MapData, MapSymbol, Marker, Measurement, Polygon, Polyline, Position, Rectangle} from "../models/mapSymbol";
|
import {Circle, MapData, MapSymbol, Marker, Measurement, Polygon, Polyline, Position, Rectangle} from "../models/mapSymbol";
|
||||||
import * as _ from 'lodash';
|
import {Md5} from 'ts-md5';
|
||||||
import {filter, map} from "rxjs/operators";
|
import {filter, map} from "rxjs/operators";
|
||||||
|
|
||||||
export const LOCATION_COLLECTION = 'Users';
|
export const LOCATION_COLLECTION = 'Users';
|
||||||
@ -22,6 +22,7 @@ export class SyncService {
|
|||||||
private saveInterval: number;
|
private saveInterval: number;
|
||||||
private username: string;
|
private username: string;
|
||||||
|
|
||||||
|
freeze = new BehaviorSubject<boolean>(false);
|
||||||
mapData = new BehaviorSubject<MapData>({});
|
mapData = new BehaviorSubject<MapData>({});
|
||||||
status = new BehaviorSubject<string>(null);
|
status = new BehaviorSubject<string>(null);
|
||||||
|
|
||||||
@ -39,10 +40,13 @@ export class SyncService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private addMapSymbol(s: MapSymbol, key: string) {
|
private addMapSymbol(s: MapSymbol, key: string) {
|
||||||
s.new = true;
|
|
||||||
let map = this.mapData.value;
|
let map = this.mapData.value;
|
||||||
if(!map[key]) map[key] = [];
|
if(!map[key]) map[key] = {};
|
||||||
map[key].push(s);
|
do {
|
||||||
|
s.updated = new Date().getTime();
|
||||||
|
s.id = Md5.hashStr(s.updated.toString()).toString();
|
||||||
|
} while (!!map[key][s.id]);
|
||||||
|
map[key][s.id] = s;
|
||||||
this.mapData.next(map);
|
this.mapData.next(map);
|
||||||
this.mapChanged = true;
|
this.mapChanged = true;
|
||||||
this.status.next('modified');
|
this.status.next('modified');
|
||||||
@ -67,8 +71,8 @@ export class SyncService {
|
|||||||
addMyLocation(location: Position) {
|
addMyLocation(location: Position) {
|
||||||
location.timestamp = new Date();
|
location.timestamp = new Date();
|
||||||
let markForSave = this.location == null;
|
let markForSave = this.location == null;
|
||||||
if(!this.locationChanged) this.locationChanged = !_.isEqual(this.location, location);
|
this.locationChanged = true;
|
||||||
if(this.locationChanged) this.location = location;
|
this.location = location;
|
||||||
if(markForSave) return this.save(false, true);
|
if(markForSave) return this.save(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,9 +90,7 @@ export class SyncService {
|
|||||||
|
|
||||||
delete(...symbols) {
|
delete(...symbols) {
|
||||||
let map = this.mapData.value;
|
let map = this.mapData.value;
|
||||||
Object.keys(map).filter(key => Array.isArray(map[key])).forEach(key => {
|
Object.keys(map).forEach(key => symbols.filter(s => !!map[key][s.id]).forEach(s => map[key][s.id].deleted = true));
|
||||||
symbols.forEach(s => map[key] = map[key].filter(ss => !_.isEqual(s, ss)))
|
|
||||||
});
|
|
||||||
this.mapData.next(map);
|
this.mapData.next(map);
|
||||||
this.mapChanged = true;
|
this.mapChanged = true;
|
||||||
this.status.next('modified');
|
this.status.next('modified');
|
||||||
@ -105,15 +107,15 @@ export class SyncService {
|
|||||||
let aMinuteAgo = new Date();
|
let aMinuteAgo = new Date();
|
||||||
aMinuteAgo.setMinutes(aMinuteAgo.getMinutes() - 1);
|
aMinuteAgo.setMinutes(aMinuteAgo.getMinutes() - 1);
|
||||||
return ref.where('timestamp', '>=', aMinuteAgo);
|
return ref.where('timestamp', '>=', aMinuteAgo);
|
||||||
}).snapshotChanges())
|
}).snapshotChanges(), this.freeze)
|
||||||
.pipe(map(data => {
|
.pipe(map(data => {
|
||||||
let newMap = data[0];
|
|
||||||
let oldMap = this.mapData.value;
|
let oldMap = this.mapData.value;
|
||||||
|
if(data[2]) return oldMap;
|
||||||
|
let newMap = data[0] || {};
|
||||||
let mergedMap = this.mergeMaps(newMap, oldMap);
|
let mergedMap = this.mergeMaps(newMap, oldMap);
|
||||||
|
|
||||||
let locations = data[1].map(doc => ({id: doc.payload.doc.id, data: <Marker>doc.payload.doc.data()}));
|
let locations = data[1].map(doc => ({id: doc.payload.doc.id, data: <Marker>doc.payload.doc.data()}));
|
||||||
locations.filter(l => l.id != username).forEach(l => {
|
locations.filter(l => l.id != username).forEach(l => {
|
||||||
if(!mergedMap.locations) mergedMap.locations = {};
|
|
||||||
mergedMap.locations[l.id] = l.data;
|
mergedMap.locations[l.id] = l.data;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -127,14 +129,20 @@ export class SyncService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mergeMaps(newMap: MapData, oldMap: MapData) {
|
mergeMaps(newMap: MapData, oldMap: MapData) {
|
||||||
let map = Object.assign({}, newMap);
|
let map: MapData = {locations: {}};
|
||||||
Object.keys(oldMap).forEach(key => {
|
Object.keys(newMap).forEach(key => {
|
||||||
if(Array.isArray(map[key])) {
|
if(!map[key]) map[key] = {};
|
||||||
if(!map[key]) map[key] = [];
|
Object.keys(newMap[key]).filter(id => !newMap[key][id].deleted)
|
||||||
oldMap[key].filter(s => !_.find(map[key], s) && s.new).forEach(s => map[key].push(s));
|
.forEach(id => map[key][id] = newMap[key][id]);
|
||||||
}
|
});
|
||||||
|
|
||||||
|
Object.keys(oldMap).filter(key => key != 'locations').forEach(key => {
|
||||||
|
if(!map[key]) map[key] = {};
|
||||||
|
Object.keys(oldMap[key]).filter(id => {
|
||||||
|
let newS = map[key][id] || false;
|
||||||
|
return newS && newS.updated > map[key][id].updated;
|
||||||
|
}).forEach(id => map[key][id] = oldMap[key][id]);
|
||||||
});
|
});
|
||||||
if(!map.locations) map.locations = {};
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +161,6 @@ export class SyncService {
|
|||||||
if(map && this.mapDoc && this.mapChanged) {
|
if(map && this.mapDoc && this.mapChanged) {
|
||||||
this.status.next('saving');
|
this.status.next('saving');
|
||||||
let map = this.mapData.value;
|
let map = this.mapData.value;
|
||||||
Object.values(map).filter(val => Array.isArray(val)).forEach(val => val.filter(s => s.new).forEach(s => delete s.new));
|
|
||||||
delete map.locations;
|
delete map.locations;
|
||||||
promises.push(this.mapDoc.set(map));
|
promises.push(this.mapDoc.set(map));
|
||||||
this.mapChanged = false;
|
this.mapChanged = false;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 15%;
|
top: 15%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
z-index: 500;
|
z-index: 5000;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11,7 +11,7 @@
|
|||||||
bottom: 15%;
|
bottom: 15%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
z-index: 500;
|
z-index: 5000;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,5 +19,5 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 2%;
|
bottom: 2%;
|
||||||
right: 2%;
|
right: 2%;
|
||||||
z-index: 500;
|
z-index: 5000;
|
||||||
}
|
}
|
||||||
|
@ -4,14 +4,14 @@
|
|||||||
|
|
||||||
.palette {
|
.palette {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 500;
|
z-index: 5000;
|
||||||
top: 60px;
|
top: 60px;
|
||||||
right: 15px;
|
right: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.share {
|
.share {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 500;
|
z-index: 5000;
|
||||||
top: 60px;
|
top: 60px;
|
||||||
right: 15px;
|
right: 15px;
|
||||||
}
|
}
|
||||||
@ -19,14 +19,14 @@
|
|||||||
.info {
|
.info {
|
||||||
background-color: #00000050;
|
background-color: #00000050;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 500;
|
z-index: 5000;
|
||||||
bottom: 15px;
|
bottom: 15px;
|
||||||
left: 15px;
|
left: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gps {
|
.gps {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 500;
|
z-index: 5000;
|
||||||
bottom: 15px;
|
bottom: 15px;
|
||||||
right: 15px;
|
right: 15px;
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@
|
|||||||
::ng-deep .mat-toolbar {
|
::ng-deep .mat-toolbar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 45px !important;
|
height: 45px !important;
|
||||||
z-index: 500;
|
z-index: 5000;
|
||||||
color: rgba(255, 255, 255, 0.54);
|
color: rgba(255, 255, 255, 0.54);
|
||||||
background-color: rgba(0, 0, 0, 0.8);
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
|
||||||
|
@ -12,9 +12,12 @@ import {ActivatedRoute} from "@angular/router";
|
|||||||
import {DimensionsDialogComponent} from "../../components/dimensionsDialog/dimensionsDialog.component";
|
import {DimensionsDialogComponent} from "../../components/dimensionsDialog/dimensionsDialog.component";
|
||||||
import {MatDialog} from "@angular/material/dialog";
|
import {MatDialog} from "@angular/material/dialog";
|
||||||
import {SyncService} from "../../services/sync.service";
|
import {SyncService} from "../../services/sync.service";
|
||||||
import {MapData, Marker} from "../../models/mapSymbol";
|
import {MapData, MapSymbol, Marker} from "../../models/mapSymbol";
|
||||||
import {Adjectives} from "../../adjectives";
|
import {Adjectives} from "../../adjectives";
|
||||||
import {Nouns} from "../../nounes";
|
import {Nouns} from "../../nounes";
|
||||||
|
import {EditSymbolComponent} from "../../components/editSymbol/editSymbol.component";
|
||||||
|
|
||||||
|
declare const L;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'map',
|
selector: 'map',
|
||||||
@ -95,28 +98,25 @@ export class MapComponent implements OnDestroy, OnInit {
|
|||||||
// Setup map repainting on updates
|
// Setup map repainting on updates
|
||||||
this.syncService.mapData.pipe(filter(s => !!s)).subscribe((map: MapData) => {
|
this.syncService.mapData.pipe(filter(s => !!s)).subscribe((map: MapData) => {
|
||||||
this.map.deleteAll();
|
this.map.deleteAll();
|
||||||
if (map.circles) map.circles.forEach(c => this.map.newCircle(c));
|
if (map.circles) Object.values(map.circles).filter(c => !c.deleted).forEach(c => this.map.newCircle(c));
|
||||||
if (map.locations) Object.values(map.locations).forEach(l => this.map.newMarker(Object.assign(l, {icon: 'dot'})));
|
if (map.locations) Object.values(map.locations).forEach(l => this.map.newMarker(Object.assign(l, {icon: 'dot', noDeleteTool: true})));
|
||||||
if (map.markers) map.markers.forEach(m => this.map.newMarker(m));
|
if (map.markers) Object.values(map.markers).filter(m => !m.deleted).forEach(m => this.map.newMarker(m));
|
||||||
if (map.measurements) map.measurements.forEach(m => this.map.newMeasurement(m));
|
if (map.measurements) Object.values(map.measurements).filter(m => !m.deleted).forEach(m => this.map.newMeasurement(m));
|
||||||
if (map.polygons) map.polygons.forEach(p => this.map.newPolygon(p));
|
if (map.polygons) Object.values(map.polygons).filter(p => !p.deleted).forEach(p => this.map.newPolygon(p));
|
||||||
if (map.polylines) map.polylines.forEach(p => this.map.newPolyline(p));
|
if (map.polylines) Object.values(map.polylines).filter(p => !p.deleted).forEach(p => this.map.newPolyline(p));
|
||||||
if (map.rectangles) map.rectangles.forEach(r => this.map.newRectangle(r));
|
if (map.rectangles) Object.values(map.rectangles).filter(r => !r.deleted).forEach(r => this.map.newRectangle(r));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle opening map symbols
|
// Handle opening map symbols
|
||||||
this.map.click.pipe(filter(e => !!e && e.item)).subscribe(ignore => {
|
this.map.click.pipe(filter(e => !!e)).subscribe(e => {
|
||||||
/*if (e.item instanceof L.Marker) {
|
if(this.sub == null && e.symbol) {
|
||||||
if (e.symbol.noSelect) return;
|
this.sub = this.bottomSheet.open(EditSymbolComponent, {data: e, disableClose: true, hasBackdrop: false}).afterDismissed().pipe(finalize(() => this.sub = null)).subscribe(symbol => {
|
||||||
this.bottomSheet.open(MarkerComponent, {data: e.symbol, hasBackdrop: false, disableClose: true});
|
this.syncService.delete(e.symbol);
|
||||||
} else if (e.item instanceof L.Circle) {
|
if(e.item instanceof L.Circle) {
|
||||||
if (e.symbol.noSelect) return;
|
this.syncService.addCircle(symbol);
|
||||||
this.bottomSheet.open(CircleComponent, {data: e.symbol, hasBackdrop: false, disableClose: true}).afterDismissed().subscribe(c => {
|
}
|
||||||
let circle = c['_symbol'];
|
|
||||||
this.map.delete(c);
|
|
||||||
this.map.newCircle(circle);
|
|
||||||
});
|
});
|
||||||
}*/
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Display location information & submit it
|
// Display location information & submit it
|
||||||
@ -172,9 +172,12 @@ export class MapComponent implements OnDestroy, OnInit {
|
|||||||
startCalibrating = (menuItem?) => {
|
startCalibrating = (menuItem?) => {
|
||||||
this.calibration = this.bottomSheet.open(CalibrateComponent, {hasBackdrop: false, disableClose: true});
|
this.calibration = this.bottomSheet.open(CalibrateComponent, {hasBackdrop: false, disableClose: true});
|
||||||
this.sub = this.calibration.afterDismissed().pipe(finalize(() => {
|
this.sub = this.calibration.afterDismissed().pipe(finalize(() => {
|
||||||
|
menuItem.enabled = false;
|
||||||
|
})).subscribe(() => {
|
||||||
this.calibration.dismiss();
|
this.calibration.dismiss();
|
||||||
this.calibration = null;
|
this.calibration = null;
|
||||||
}), filter(menuItem => !!menuItem)).subscribe(() => menuItem.enabled = false);
|
this.sub = null;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
startCircle = menuItem => {
|
startCircle = menuItem => {
|
||||||
@ -182,6 +185,7 @@ export class MapComponent implements OnDestroy, OnInit {
|
|||||||
let dimensions = await this.dialog.open(DimensionsDialogComponent, {data: ['Radius (m)'], panelClass: 'pb-0'}).afterClosed().toPromise();
|
let dimensions = await this.dialog.open(DimensionsDialogComponent, {data: ['Radius (m)'], panelClass: 'pb-0'}).afterClosed().toPromise();
|
||||||
if(!dimensions) return;
|
if(!dimensions) return;
|
||||||
menuItem.enabled = false;
|
menuItem.enabled = false;
|
||||||
|
this.sub = null;
|
||||||
let circle = {latlng: e.latlng, radius: dimensions[0]};
|
let circle = {latlng: e.latlng, radius: dimensions[0]};
|
||||||
this.syncService.addCircle(circle);
|
this.syncService.addCircle(circle);
|
||||||
});
|
});
|
||||||
@ -201,7 +205,7 @@ export class MapComponent implements OnDestroy, OnInit {
|
|||||||
this.showPalette = false;
|
this.showPalette = false;
|
||||||
this.map.lock(true);
|
this.map.lock(true);
|
||||||
})).subscribe(e => {
|
})).subscribe(e => {
|
||||||
let p = {latlng: [e.latlng], noDelete: true, color: this.drawColor, weight: 8};
|
let p = {latlng: [e.latlng], noClick: true, noDelete: true, color: this.drawColor, weight: 8};
|
||||||
let polyline = this.map.newPolyline(p);
|
let polyline = this.map.newPolyline(p);
|
||||||
let drawingSub = this.map.touch.pipe(filter(e => e.type == 'move')).subscribe(e => polyline.addLatLng(e.latlng));
|
let drawingSub = this.map.touch.pipe(filter(e => e.type == 'move')).subscribe(e => polyline.addLatLng(e.latlng));
|
||||||
this.map.touch.pipe(filter(e => e.type == 'end'), take(1)).subscribe(() => {
|
this.map.touch.pipe(filter(e => e.type == 'end'), take(1)).subscribe(() => {
|
||||||
@ -217,12 +221,13 @@ export class MapComponent implements OnDestroy, OnInit {
|
|||||||
startMarker = menuItem => {
|
startMarker = menuItem => {
|
||||||
this.sub = this.map.click.pipe(skip(1), take(1)).subscribe(e => {
|
this.sub = this.map.click.pipe(skip(1), take(1)).subscribe(e => {
|
||||||
menuItem.enabled = false;
|
menuItem.enabled = false;
|
||||||
|
this.sub = null;
|
||||||
let marker: Marker = {latlng: e.latlng};
|
let marker: Marker = {latlng: e.latlng};
|
||||||
this.syncService.addMarker(marker);
|
this.syncService.addMarker(marker);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
startMeasuring = menuItem => {
|
startMeasuring = () => {
|
||||||
let lastPoint;
|
let lastPoint;
|
||||||
this.sub = this.map.click.pipe(skip(1), finalize(() => this.map.delete(lastPoint))).subscribe(e => {
|
this.sub = this.map.click.pipe(skip(1), finalize(() => this.map.delete(lastPoint))).subscribe(e => {
|
||||||
if (lastPoint) {
|
if (lastPoint) {
|
||||||
@ -253,6 +258,7 @@ export class MapComponent implements OnDestroy, OnInit {
|
|||||||
this.sub = this.map.click.pipe(skip(1), take(2), finalize(() => this.map.delete(lastPoint))).subscribe(e => {
|
this.sub = this.map.click.pipe(skip(1), take(2), finalize(() => this.map.delete(lastPoint))).subscribe(e => {
|
||||||
if (lastPoint) {
|
if (lastPoint) {
|
||||||
menuItem.enabled = false;
|
menuItem.enabled = false;
|
||||||
|
this.sub = null;
|
||||||
let rect = {latlng: {lat: lastPoint.getLatLng().lat, lng: lastPoint.getLatLng().lng}, latlng2: e.latlng};
|
let rect = {latlng: {lat: lastPoint.getLatLng().lat, lng: lastPoint.getLatLng().lng}, latlng2: e.latlng};
|
||||||
this.syncService.addRectangle(rect);
|
this.syncService.addRectangle(rect);
|
||||||
return this.map.delete(lastPoint);
|
return this.map.delete(lastPoint);
|
||||||
|
@ -14,6 +14,10 @@ body {
|
|||||||
background-color: black;
|
background-color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.curs-pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.d-relative {
|
.d-relative {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
@ -47,3 +51,22 @@ a[href^="https://maps.google.com/maps"]{display:none !important}
|
|||||||
background: none !important;
|
background: none !important;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cdk-overlay-container {
|
||||||
|
z-index: 5500 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-tooltip {
|
||||||
|
background: rgba(0, 0, 0, 0.6) !important;
|
||||||
|
border-color: rgba(0, 0, 0, 0.6) !important;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-popup-content-wrapper {
|
||||||
|
background: rgba(0, 0, 0, 0.6) !important;
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-popup-tip {
|
||||||
|
background: rgba(0, 0, 0, 0.6) !important;
|
||||||
|
}
|
||||||
|
@ -7621,6 +7621,11 @@ try-require@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/try-require/-/try-require-1.2.1.tgz#34489a2cac0c09c1cc10ed91ba011594d4333be2"
|
resolved "https://registry.yarnpkg.com/try-require/-/try-require-1.2.1.tgz#34489a2cac0c09c1cc10ed91ba011594d4333be2"
|
||||||
integrity sha1-NEiaLKwMCcHMEO2RugEVlNQzO+I=
|
integrity sha1-NEiaLKwMCcHMEO2RugEVlNQzO+I=
|
||||||
|
|
||||||
|
ts-md5@^1.2.5:
|
||||||
|
version "1.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/ts-md5/-/ts-md5-1.2.5.tgz#4cb00ddf687251a2aba09922f95f766bf6486a15"
|
||||||
|
integrity sha512-0E8UHsfEmK9CICH47YLVIN6/wtGJJ7/8XiAbUBszTHxeucglueTgPn86D8Ec3UG9tYWTICVY/EuUGQ+Xa2iSEA==
|
||||||
|
|
||||||
ts-node@~7.0.0:
|
ts-node@~7.0.0:
|
||||||
version "7.0.1"
|
version "7.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.1.tgz#9562dc2d1e6d248d24bc55f773e3f614337d9baf"
|
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.1.tgz#9562dc2d1e6d248d24bc55f773e3f614337d9baf"
|
||||||
|
Loading…
Reference in New Issue
Block a user