Skip to content

Commit

Permalink
Improve public POI performance and allow offline usage #1169 - switch…
Browse files Browse the repository at this point in the history
… to use geojson file for offline use and site regular use.
  • Loading branch information
HarelM committed May 26, 2020
1 parent 66f955e commit 6c60d47
Show file tree
Hide file tree
Showing 26 changed files with 448 additions and 463 deletions.
8 changes: 5 additions & 3 deletions IsraelHiking.Web/sources/application/application.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ import { ToastService } from "./services/toast.service";
import { ElevationProvider } from "./services/elevation.provider";
import { SearchResultsProvider } from "./services/search-results.provider";
import { GeoJsonParser } from "./services/geojson.parser";
import { CategoriesLayerFactory } from "./services/layers/categories-layers.factory";
import { DragAndDropService } from "./services/drag-and-drop.service";
import { PoiService } from "./services/poi.service";
import { GeoLocationService } from "./services/geo-location.service";
Expand Down Expand Up @@ -168,6 +167,8 @@ import { SecuredImageComponent } from "./components/secured-image.component";
import { ConfigurationDialogComponent } from "./components/dialogs/configuration-dialog.component";
import { DownloadProgressDialogComponent } from "./components/dialogs/download-progress-dialog.component";
import { UseAppDialogComponent } from "./components/dialogs/use-app-dialog.component";
import { CategoriesGroupComponent } from "./components/sidebar/categories-group.component";

// variables and functions
import { routes } from "./routes";

Expand Down Expand Up @@ -257,7 +258,8 @@ export function getWindow() { return window; }
AutomaticLayerPresentationComponent,
ConfigurationDialogComponent,
DownloadProgressDialogComponent,
UseAppDialogComponent
UseAppDialogComponent,
CategoriesGroupComponent
],
providers: [
GestureConfig,
Expand Down Expand Up @@ -287,7 +289,6 @@ export function getWindow() { return window; }
ElevationProvider,
SearchResultsProvider,
GeoJsonParser,
CategoriesLayerFactory,
DragAndDropService,
PoiService,
GeoLocationService,
Expand Down Expand Up @@ -381,6 +382,7 @@ export function getWindow() { return window; }
ConfigurationDialogComponent,
DownloadProgressDialogComponent,
UseAppDialogComponent,
CategoriesGroupComponent,
NameInUseValidatorDirective,
ImageCaptureDirective,
OfflineImagePipe
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,42 +18,38 @@
[key]="overlay.key">
</auto-layer>
</div>
<div *ngFor="let categoriesType of categoriesTypes">
<mgl-geojson-source #clusterSource
[id]="poiSourceName[categoriesType]"
[data]="poiGeoJsonData[categoriesType]"
[cluster]="true"
[clusterMaxZoom]="13"
[clusterRadius]="100">
</mgl-geojson-source>
<mgl-markers-for-clusters *ngIf="isVisible(categoriesType)"
[source]="poiSourceName[categoriesType]"
#cluster>
<ng-template mglPoint let-feature>
<span class="fa-stack fa-lg cursor-pointer marker-icon" (click)="openPoi(feature.properties.id, $event)" (mouseover)="hoverFeature = feature" (mouseout)="hoverFeature = null">
<span *ngIf="feature.properties.hasExtraData" class="has-extra-data" [style.background]="feature.properties.iconColor"></span>
<i class="fa icon-map-marker-rect fa-stack-2x" style="color: white; text-shadow: 3px 3px 3px #000;"></i>
<i class="fa {{feature.properties.icon}} fa-stack-1x stack-icon-top stack-icon-large" [style.color]="feature.properties.iconColor"></i>
</span>
</ng-template>
<ng-template mglClusterPoint let-feature>
<div class="marker-cluster" (click)="toggleClusterPopup($event, feature, clusterSource)">
{{ feature.properties?.point_count }}
</div>
</ng-template>
</mgl-markers-for-clusters>
</div>
<mgl-geojson-source #clusterSource
id="poi-cluster-source"
[data]="poiGeoJsonData"
[cluster]="true"
[clusterMaxZoom]="13"
[clusterRadius]="100">
</mgl-geojson-source>
<mgl-markers-for-clusters source="poi-cluster-source">
<ng-template mglPoint let-feature>
<span class="fa-stack fa-lg cursor-pointer marker-icon" (click)="openPoi(feature.properties.poiId, $event)" (mouseover)="hoverFeature = feature" (mouseout)="hoverFeature = null">
<span *ngIf="feature.properties.hasExtraData" class="has-extra-data" [style.background]="feature.properties.poiIconColor"></span>
<i class="fa icon-map-marker-rect fa-stack-2x" style="color: white; text-shadow: 3px 3px 3px #000;"></i>
<i class="fa {{feature.properties.poiIcon}} fa-stack-1x stack-icon-top stack-icon-large" [style.color]="feature.properties.poiIconColor"></i>
</span>
</ng-template>
<ng-template mglClusterPoint let-feature>
<div class="marker-cluster" (click)="toggleClusterPopup($event, feature, clusterSource)">
{{ feature.properties?.point_count }}
</div>
</ng-template>
</mgl-markers-for-clusters>
<mgl-popup *ngIf="selectedCluster" [lngLat]="selectedCluster.geometry.coordinates" (close)="clearSelectedClusterPopup()" [closeOnClick]="true">
<cluster-overlay [points]="clusterPoints" (closed)="clearSelectedClusterPopup()"></cluster-overlay>
</mgl-popup>
<mgl-popup *ngIf="hoverFeature" [lngLat]="hoverFeature.geometry.coordinates" [closeButton]="false" [offset]="[0, -20]">
<span [dir]="resources.getDirection(hoverFeature.properties.title)">{{hoverFeature.properties.title}}</span>
</mgl-popup>
<mgl-marker *ngIf="selectedPoiFeature" [lngLat]="selectedPoiFeature.geometry.coordinates" anchor="bottom">
<span class="fa-stack fa-lg cursor-pointer" (click)="openPoi(selectedPoiFeature.properties.id, $event)" (mouseover)="hoverFeature = selectedPoiFeature" (mouseout)="hoverFeature = null">
<span *ngIf="selectedPoiFeature.properties.hasExtraData" class="has-extra-data" [style.background]="selectedPoiFeature.properties.iconColor"></span>
<span class="fa-stack fa-lg cursor-pointer" (click)="openPoi(selectedPoiFeature.properties.poiId, $event)" (mouseover)="hoverFeature = selectedPoiFeature" (mouseout)="hoverFeature = null">
<span *ngIf="selectedPoiFeature.properties.hasExtraData" class="has-extra-data" [style.background]="selectedPoiFeature.properties.poiIconColor"></span>
<i class="fa icon-map-marker-rect fa-stack-2x" style="color: white; text-shadow: 3px 3px 3px #000;"></i>
<i class="fa {{selectedPoiFeature.properties.icon}} fa-stack-1x stack-icon-top stack-icon-large" [style.color]="selectedPoiFeature.properties.iconColor"></i>
<i class="fa {{selectedPoiFeature.properties.poiIcon}} fa-stack-1x stack-icon-top stack-icon-large" [style.color]="selectedPoiFeature.properties.poiIconColor"></i>
</span>
</mgl-marker>
<mgl-geojson-source id="selected-poi" [data]="selectedPoiGeoJson"></mgl-geojson-source>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { Component, OnInit, ViewChildren, QueryList } from "@angular/core";
import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { Observable } from "rxjs";
import { GeoJSONSourceComponent } from "ngx-mapbox-gl";
import { MarkersForClustersComponent } from "ngx-mapbox-gl/lib/markers-for-clusters/markers-for-clusters.component";
import { select } from "@angular-redux/store";

import { PoiService, CategoriesType } from "../../services/poi.service";
import { PoiService } from "../../services/poi.service";
import { LayersService } from "../../services/layers/layers.service";
import { CategoriesLayerFactory } from "../../services/layers/categories-layers.factory";
import { RouteStrings } from "../../services/hash.service";
import { BaseMapComponent } from "../base-map.component";
import { ResourcesService } from "../../services/resources.service";
Expand All @@ -21,17 +19,12 @@ import { ApplicationState, Overlay, PointOfInterest, PointOfInterestExtended } f
export class LayersViewComponent extends BaseMapComponent implements OnInit {
private static readonly MAX_MENU_POINTS_IN_CLUSTER = 7;

@ViewChildren("cluster")
public clustersComponents: QueryList<MarkersForClustersComponent>;

public categoriesTypes: CategoriesType[];
public poiGeoJsonData: { [category: string]: GeoJSON.FeatureCollection<GeoJSON.Point> };
public poiGeoJsonData: GeoJSON.FeatureCollection<GeoJSON.Point>;
public selectedPoiFeature: GeoJSON.Feature<GeoJSON.Point>;
public selectedPoiGeoJson: GeoJSON.FeatureCollection;
public selectedCluster: GeoJSON.Feature<GeoJSON.Point>;
public clusterPoints: PointOfInterest[];
public hoverFeature: GeoJSON.Feature<GeoJSON.Point>;
public poiSourceName: { [categoriesType: string]: string };

@select((state: ApplicationState) => state.layersState.overlays)
public overlays: Observable<Overlay[]>;
Expand All @@ -42,43 +35,27 @@ export class LayersViewComponent extends BaseMapComponent implements OnInit {
constructor(resources: ResourcesService,
private readonly router: Router,
private readonly layersService: LayersService,
private readonly categoriesLayerFactory: CategoriesLayerFactory,
private readonly poiService: PoiService) {
private readonly poiService: PoiService
) {
super(resources);
this.categoriesTypes = this.poiService.getCategoriesTypes();
this.selectedCluster = null;
this.hoverFeature = null;
this.selectedPoiFeature = null;
this.selectedPoiGeoJson = {
type: "FeatureCollection",
features: []
};
this.poiGeoJsonData = {};
this.poiSourceName = {};
for (let categoriesType of this.categoriesTypes) {
this.poiGeoJsonData[categoriesType] = { type: "FeatureCollection", features: [] };
this.poiSourceName[categoriesType] = "poiSource" + categoriesType.replace(/\s/g, "");
}
}

public getBaseLayer() {
return this.layersService.getSelectedBaseLayer();
}

public isVisible(categoriesType: CategoriesType) {
return this.categoriesLayerFactory.get(categoriesType).isVisible();
}

public ngOnInit() {
for (let categoriesType of this.categoriesTypes) {
this.categoriesLayerFactory.get(categoriesType).markersLoaded.subscribe(() => {
let features = this.categoriesLayerFactory.get(categoriesType).pointsOfInterest.map(p => this.poiToFeature(p));
this.poiGeoJsonData[categoriesType] = {
type: "FeatureCollection",
features
};
});
}
this.poiGeoJsonData = this.poiService.poiGeojsonFiltered;
this.poiService.poisChanged.subscribe(() => {
this.poiGeoJsonData = this.poiService.poiGeojsonFiltered;
});
this.selectedPoi$.subscribe((poi) => this.onSelectedPoiChanged(poi));
}

Expand All @@ -96,13 +73,13 @@ export class LayersViewComponent extends BaseMapComponent implements OnInit {
}

private poiToFeature(p: PointOfInterest): GeoJSON.Feature<GeoJSON.Point> {
let id = p.source + "__" + p.id;
let id = p.source + "_" + p.id;
return {
type: "Feature",
properties: {
id,
icon: p.icon,
iconColor: p.iconColor,
poiId: id,
poiIcon: p.icon,
poiIconColor: p.iconColor,
title: p.title,
hasExtraData: p.hasExtraData
},
Expand Down Expand Up @@ -135,10 +112,10 @@ export class LayersViewComponent extends BaseMapComponent implements OnInit {
this.selectedCluster = feature;
this.clusterPoints = features.map(f => {
let properties = f.properties;
let sourceAndId = this.getSourceAndId(f.properties.id.toString());
let sourceAndId = this.getSourceAndId(f.properties.poiId);
return {
icon: properties.icon,
iconColor: properties.iconColor,
icon: properties.poiIcon,
iconColor: properties.poiIconColor,
title: properties.title,
id: sourceAndId.id,
source: sourceAndId.source
Expand All @@ -147,8 +124,8 @@ export class LayersViewComponent extends BaseMapComponent implements OnInit {
}

private getSourceAndId(sourceAndId: string): { source: string, id: string } {
let poiSource = sourceAndId.split("__")[0];
let id = sourceAndId.split("__")[1];
let poiSource = sourceAndId.split("_")[0];
let id = sourceAndId.replace(poiSource + "_", "");
return {
source: poiSource,
id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,6 @@ export class RoutesComponent extends BaseMapComponent implements AfterViewInit {

public routeLineMouseEnter(event) {
this.host.mapInstance.getCanvas().style.cursor = "pointer";
console.log(event.features);
this.routeLineMouseOver(event);
}

Expand Down Expand Up @@ -310,7 +309,7 @@ export class RoutesComponent extends BaseMapComponent implements AfterViewInit {
}
let selectedRoute = this.selectedRouteService.getSelectedRoute();
let clickedRoute = this.selectedRouteService.getRouteById(event.features[0].properties.id);
if (clickedRoute != null && clickedRoute != selectedRoute && !this.isEditMode()) {
if (clickedRoute != null && clickedRoute !== selectedRoute && !this.isEditMode()) {
this.selectedRouteService.setSelectedRoute(clickedRoute.id);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ export class RouteStatisticsComponent extends BaseMapComponent implements OnInit
.attr("x1", 0)
.attr("x2", this.chartElements.width)
.attr("y1", this.chartElements.height / 2)
.attr("y2", this.chartElements.height / 2)
.attr("y2", this.chartElements.height / 2);
}
}

Expand Down Expand Up @@ -682,7 +682,7 @@ export class RouteStatisticsComponent extends BaseMapComponent implements OnInit
chartTransition.select(".y-axis")
.call(d3.axisLeft(this.chartElements.yScale).ticks(5) as any)
.duration(duration);
let slopeData = []
let slopeData = [];
if (this.isSlopeOn && data.length > 0) {
slopeData = this.statistics.points.map(p => [p.coordinate[0], p.slope] as [number, number]);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<mat-expansion-panel [expanded]="getExpandState()" (opened)="expand()" (closed)="collapse()">
<mat-expansion-panel-header>
<button mat-button (click)="toggleVisibility()" angulartics2On="click" angularticsCategory="Layers" angularticsAction="Toggle visiblity of category layer {{categoriesGroup.type}}">
<i class="fa fa-lg" [ngClass]="{'icon-eye': categoriesGroup.visible, 'icon-eye-slash': !categoriesGroup.visible}"></i>
</button>
<p>{{resources.translate(categoriesGroup.type)}}</p>
</mat-expansion-panel-header>
<div *ngFor="let category of categoriesGroup.categories">
<div fxLayout="row" class="cursor-pointer row-hover" (click)="toggleCategory(category)" angulartics2On="click" angularticsCategory="Layers" angularticsAction="Toggle category visibility {{category.name}}">
<div fxFlex="10"></div>
<div fxFlex="15">
<i class="fa" [ngClass]="{'icon-eye': category.visible, 'icon-eye-slash': !category.visible}"></i>
</div>
<div fxFlex="15">
<i class="fa" [ngClass]="category.icon" [style.color]="category.color"></i>
</div>
<div fxFlex>
<span>{{resources.translate(category.name)}}</span>
</div>
</div>
</div>
</mat-expansion-panel>
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Component, Input } from "@angular/core";
import { NgRedux } from "@angular-redux/store";

import { BaseMapComponent } from "../base-map.component";
import { ResourcesService } from "../../services/resources.service";
import {
ExpandGroupAction,
CollapseGroupAction,
SetCategoryVisibilityAction,
SetCategoriesGroupVisibilityAction
} from "../../reducres/layers.reducer";
import { ApplicationState, CategoriesGroup, Category } from "../../models/models";

@Component({
selector: "categories-group",
templateUrl: "./categories-group.component.html"
})
export class CategoriesGroupComponent extends BaseMapComponent {

@Input()
public categoriesGroup: CategoriesGroup;

constructor(resources: ResourcesService,
private readonly ngRedux: NgRedux<ApplicationState>) {
super(resources);
}

public expand() {
this.ngRedux.dispatch(new ExpandGroupAction({ name: this.categoriesGroup.type }));
}

public collapse() {
this.ngRedux.dispatch(new CollapseGroupAction({ name: this.categoriesGroup.type }));
}

public getExpandState(): boolean {
return this.ngRedux.getState().layersState.expanded.find(l => l === this.categoriesGroup.type) != null;
}

public toggleCategory(category: Category) {
this.ngRedux.dispatch(new SetCategoryVisibilityAction({
groupType: this.categoriesGroup.type,
name: category.name,
visible: !category.visible
}));
}

public toggleVisibility() {
this.ngRedux.dispatch(new SetCategoriesGroupVisibilityAction({
groupType: this.categoriesGroup.type,
visible: !this.categoriesGroup.visible
}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,31 +55,9 @@
</div>
</div>
</mat-expansion-panel>
<ng-container *ngFor="let categoriesType of categoriesTypes">
<mat-expansion-panel [expanded]="getExpandState(categoriesType)" (opened)="expand(categoriesType)" (closed)="collapse(categoriesType)">
<mat-expansion-panel-header>
<button mat-button (click)="toggleCategoriesLayerVisibility(categoriesType, $event)" angulartics2On="click" angularticsCategory="Layers" angularticsAction="Toggle visiblity of category layer {{categoriesType}}">
<i class="fa fa-lg" [ngClass]="{'icon-eye': isCategoriesLayerVisible(categoriesType), 'icon-eye-slash': !isCategoriesLayerVisible(categoriesType)}"></i>
</button>
<p>{{resources.translate(categoriesType)}}</p>
</mat-expansion-panel-header>
<div *ngFor="let category of getCategories(categoriesType)">
<div fxLayout="row" class="cursor-pointer row-hover" (click)="toggleCategory(categoriesType, category)" angulartics2On="click" angularticsCategory="Layers" angularticsAction="Toggle category visibility {{category.name}}">
<div fxFlex="10"></div>
<div fxFlex="15">
<i class="fa" [ngClass]="{'icon-eye': category.visible, 'icon-eye-slash': !category.visible}"></i>
</div>
<div fxFlex="15">
<i class="fa" [ngClass]="category.icon" [style.color]="category.color"></i>
</div>
<div fxFlex>
<span>{{resources.translate(category.name)}}</span>
</div>
</div>
</div>
</mat-expansion-panel>
<ng-container *ngFor="let categoriesGroup of (categoriesGroups | async)">
<categories-group [categoriesGroup]="categoriesGroup"></categories-group>
</ng-container>

<mat-expansion-panel [expanded]="getExpandState('Private Routes')" (opened)="expand('Private Routes')" (closed)="collapse('Private Routes')">
<mat-expansion-panel-header>
<button mat-button (click)="addRoute($event)" matTooltip="{{resources.addRoute}}" matTooltipPosition="below" angulartics2On="click" angularticsCategory="Layers" angularticsAction="Add route"><i class="fa fa-lg icon-plus"></i></button>
Expand Down
Loading

0 comments on commit 6c60d47

Please sign in to comment.