Skip to content

Commit

Permalink
Move Points of interest to use tiles (#1979)
Browse files Browse the repository at this point in the history
* Initial commit to move from indexdb and files to tiles

* Update to use the new pois end point

* Improve geometry and language and allow tests to run

* Support place icon

* Fix lint, add offline support

* Fix tests, remove unneeded field from state

* Fix offline address for pmtiles.

* Fix typo which caused a crash

* Improve properties

* For OSM elements - get the data from OSM and wikipedia.

* Add support for external sources

* Fix poi source layer name

* Fix test

* fix lint

* Improve handling of OSM returned data

* Fix tomb, used cloudflare source.

* Final fixes in links to points

* Fix lint

* Fix tests

* Adds the ability to merge ways with the same name

* Allow more distance between ways - 30 meters

* Improve how places will look using overpass turbo

* Added trails pois source and relevant handling

* Fix test

* Improve location of none point POIs, fix missing image and source link fixes.

* Sort out mtb name related routes.

* Added support for inature tag

* Remove mtb_name, added improved line merge

* Update inature reference according to discussion

* Fix lint

* Add iNature service to client side.

* Fix initialization, Add iNature route support.

* Fix lint.

* Use simplified regexp notation.

* Make regexp case insensitive.

* Rename iNature.service.ts to inature.service.ts

* Add logging

* Add wikidata service to present wikidata point online.

* Fix failure in test

* Update package to use latest ngx-maplibre, use poi source from server.

* Fix hiking icon

* Add removal of duplicated points

* Fix issue with recording by upgrading the ngx-maplibre library

* Fix typos

* Fix test due to deduplication code.

* Remove zoom 10 limitation for POIs.

* remove redundant console.log

* Allow loading a feature from an in-mempry tile

* Improve typing, add timeout.

* Improve coverage for icon selection

* Fix lint

* Run lint after running the tests

* Add test coverage to iNature service

* Fix lint

* Improve coverage for geojson utils
  • Loading branch information
HarelM authored Oct 31, 2024
1 parent ec5112d commit 16e8cfa
Show file tree
Hide file tree
Showing 25 changed files with 2,022 additions and 632 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ jobs:
run: |
cd IsraelHiking.Web
npm ci
npx ng lint
npm run test-ci
npx ng lint
- name: Codecov upload
if: always()
uses: codecov/codecov-action@v4
Expand Down
18 changes: 9 additions & 9 deletions IsraelHiking.Web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions IsraelHiking.Web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"@mapbox/mapbox-gl-rtl-text": "0.2.3",
"@mapbox/polyline": "^1.2.1",
"@mapbox/vector-tile": "^1.3.1",
"@maplibre/ngx-maplibre-gl": "^18.0.0",
"@maplibre/ngx-maplibre-gl": "^18.1.2",
"@ng-idle/core": "^15.0.0",
"@ngxs/store": "^18.1.1",
"@nicky-lenaers/ngx-scroll-to": "^14.0.0",
Expand Down Expand Up @@ -72,7 +72,7 @@
"linear-interpolator": "^1.0.2",
"lodash-es": "^4.17.21",
"lottie-web": "^5.12.2",
"maplibre-gl": "^4.6.0",
"maplibre-gl": "^4.7.0",
"minisearch": "^7.1.0",
"ngx-infinite-scroll": "^18.0.0",
"ngx-lottie": "^12.0.0",
Expand Down
4 changes: 4 additions & 0 deletions IsraelHiking.Web/src/application/application.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ import { OverpassTurboService } from "./services/overpass-turbo.service";
import { ImageAttributionService } from "./services/image-attribution.service";
import { PmTilesService } from "./services/pmtiles.service";
import { ApplicationUpdateService } from "./services/application-update.service";
import { INatureService } from "./services/inature.service";
import { WikidataService } from "./services/wikidata.service";
import { OsmAddressesService } from "./services/osm-addresses.service";
import { LocationService } from "./services/location.service";
// interactions
Expand Down Expand Up @@ -309,6 +311,8 @@ const initializeApplication = (injector: Injector) => async () => {
ImageAttributionService,
PmTilesService,
ApplicationUpdateService,
INatureService,
WikidataService,
OsmAddressesService,
LocationService,
AudioPlayerFactory,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { SpatialService } from "../../services/spatial.service";
import { NavigateHereService } from "../../services/navigate-here.service";
import { SetSelectedPoiAction } from "../../reducers/poi.reducer";
import { AddPrivatePoiAction } from "../../reducers/routes.reducer";
import { GeoJSONUtils } from "../../services/geojson-utils";
import type { ApplicationState, LatLngAlt, LinkData, Overlay } from "../../models/models";

@Component({
Expand All @@ -25,7 +26,7 @@ import type { ApplicationState, LatLngAlt, LinkData, Overlay } from "../../model
export class LayersViewComponent implements OnInit {
private static readonly MAX_MENU_POINTS_IN_CLUSTER = 7;

public poiGeoJsonData: GeoJSON.FeatureCollection<GeoJSON.Point>;
public poiGeoJsonData: GeoJSON.FeatureCollection<GeoJSON.Geometry>;
public selectedPoiFeature: GeoJSON.Feature<GeoJSON.Point> = null;
public selectedPoiGeoJson: Immutable<GeoJSON.FeatureCollection> = {
type: "FeatureCollection",
Expand Down Expand Up @@ -127,11 +128,11 @@ export class LayersViewComponent implements OnInit {
}

public getTitle(feature: GeoJSON.Feature<GeoJSON.Point>): string {
return this.poiService.getTitle(feature, this.resources.getCurrentLanguageCodeSimplified());
return GeoJSONUtils.getTitle(feature, this.resources.getCurrentLanguageCodeSimplified());
}

public hasExtraData(feature: GeoJSON.Feature<GeoJSON.Point>): boolean {
return this.poiService.hasExtraData(feature, this.resources.getCurrentLanguageCodeSimplified());
return GeoJSONUtils.hasExtraData(feature, this.resources.getCurrentLanguageCodeSimplified());
}

public isCoordinatesFeature(feature: Immutable<GeoJSON.Feature>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Component, inject, input, output } from "@angular/core";
import { Router } from "@angular/router";

import { ResourcesService } from "../../services/resources.service";
import { PoiService } from "../../services/poi.service";
import { RouteStrings } from "../../services/hash.service";
import { GeoJSONUtils } from "../../services/geojson-utils";

@Component({
selector: "cluster-overlay",
Expand All @@ -18,14 +18,13 @@ export class ClusterOverlayComponent {
public readonly resources = inject(ResourcesService);

private readonly router = inject(Router);
private readonly poiService = inject(PoiService);

public getTitle(feature: GeoJSON.Feature) {
return this.poiService.getTitle(feature, this.resources.getCurrentLanguageCodeSimplified());
return GeoJSONUtils.getTitle(feature, this.resources.getCurrentLanguageCodeSimplified());
}

public hasExtraData(feature: GeoJSON.Feature): boolean {
return this.poiService.hasExtraData(feature, this.resources.getCurrentLanguageCodeSimplified());
return GeoJSONUtils.hasExtraData(feature, this.resources.getCurrentLanguageCodeSimplified());
}

public clickOnItem(feature: GeoJSON.Feature) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { GeoJsonParser } from "../../../services/geojson.parser";
import { sidebarAnimate } from "../sidebar.component";
import { AddRouteAction, AddPrivatePoiAction } from "../../../reducers/routes.reducer";
import { SetSelectedPoiAction, SetUploadMarkerDataAction, SetSidebarAction } from "../../../reducers/poi.reducer";
import { GeoJSONUtils } from "../../../services/geojson-utils";
import type {
LinkData,
LatLngAlt,
Expand Down Expand Up @@ -176,13 +177,13 @@ export class PublicPoiSidebarComponent implements OnDestroy {

private initFromFeature(feature: GeoJSON.Feature) {
this.fullFeature = feature;
this.latlng = this.poiService.getLocation(feature);
this.latlng = GeoJSONUtils.getLocation(feature);
this.sourceImageUrls = this.getSourceImageUrls(feature);
this.shareLinks = this.poiService.getPoiSocialLinks(feature);
this.contribution = this.poiService.getContribution(feature);
this.info = this.poiService.getEditableDataFromFeature(feature);
const language = this.resources.getCurrentLanguageCodeSimplified();
this.titleService.set(this.poiService.getTitle(feature, language));
this.titleService.set(GeoJSONUtils.getTitle(feature, language));
}

private getSourceImageUrls(feature: GeoJSON.Feature): SourceImageUrlPair[] {
Expand Down Expand Up @@ -224,8 +225,8 @@ export class PublicPoiSidebarComponent implements OnDestroy {
return "";
}
const language = this.resources.getCurrentLanguageCodeSimplified();
const description = this.poiService.getDescription(this.fullFeature, language) ||
this.poiService.getExternalDescription(this.fullFeature, language);
const description = GeoJSONUtils.getDescription(this.fullFeature, language) ||
GeoJSONUtils.getExternalDescription(this.fullFeature, language);
if (description) {
return description;
}
Expand Down Expand Up @@ -324,8 +325,8 @@ export class PublicPoiSidebarComponent implements OnDestroy {
}

public navigateHere() {
const location = this.poiService.getLocation(this.fullFeature);
const title = this.poiService.getTitle(this.fullFeature, this.resources.getCurrentLanguageCodeSimplified());
const location = GeoJSONUtils.getLocation(this.fullFeature);
const title = GeoJSONUtils.getTitle(this.fullFeature, this.resources.getCurrentLanguageCodeSimplified());
this.navigateHereService.addNavigationSegment(location, title);
this.close();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
* Maps last modified date
*/
lastModifiedDate: Date;
/**
* Points of interest last modified date
*/
poisLastModifiedDate: Date;
/**
* Shares last modified date
*/
Expand Down
1 change: 0 additions & 1 deletion IsraelHiking.Web/src/application/reducers/initial-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ export const initialState =
offlineState: {
isOfflineAvailable: false,
lastModifiedDate: null,
poisLastModifiedDate: null,
shareUrlsLastModifiedDate: null,
uploadPoiQueue: [],
isPmtilesDownloaded: false
Expand Down
13 changes: 0 additions & 13 deletions IsraelHiking.Web/src/application/reducers/offline.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ export class SetOfflineMapsLastModifiedDateAction {
constructor(public lastModifiedDate: Date) {}
}

export class SetOfflinePoisLastModifiedDateAction {
public static type = this.prototype.constructor.name;
constructor(public lastModifiedDate: Date) {}
}

export class SetShareUrlsLastModifiedDateAction {
public static type = this.prototype.constructor.name;
constructor(public lastModifiedDate: Date) {}
Expand Down Expand Up @@ -59,14 +54,6 @@ export class OfflineReducer {
}));
}

@Action(SetOfflinePoisLastModifiedDateAction)
public setOfflinePoisLastModifiedDate(ctx: StateContext<OfflineState>, action: SetOfflinePoisLastModifiedDateAction) {
ctx.setState(produce(ctx.getState(), lastState => {
lastState.poisLastModifiedDate = action.lastModifiedDate;
return lastState;
}));
}

@Action(SetShareUrlsLastModifiedDateAction)
public setShareUrlsLastModifiedDate(ctx: StateContext<OfflineState>, action: SetShareUrlsLastModifiedDateAction) {
ctx.setState(produce(ctx.getState(), lastState => {
Expand Down
58 changes: 7 additions & 51 deletions IsraelHiking.Web/src/application/services/database.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ export class DatabaseService {
private static readonly STATE_DB_NAME = "State";
private static readonly STATE_TABLE_NAME = "state";
private static readonly STATE_DOC_ID = "state";
private static readonly POIS_DB_NAME = "PointsOfInterest";
private static readonly POIS_TABLE_NAME = "pois";
private static readonly POIS_UPLOAD_QUEUE_DB_NAME = "UploadQueue";
private static readonly POIS_UPLOAD_QUEUE_TABLE_NAME = "uploadQueue";
private static readonly POIS_ID_COLUMN = "properties.poiId";
private static readonly POIS_LOCATION_COLUMN = "[properties.poiGeolocation.lat+properties.poiGeolocation.lon]";
private static readonly IMAGES_DB_NAME = "Images";
private static readonly IMAGES_TABLE_NAME = "images";
private static readonly SHARE_URLS_DB_NAME = "ShareUrls";
Expand All @@ -36,7 +34,7 @@ export class DatabaseService {
private static readonly TRACES_TABLE_NAME = "traces";

private stateDatabase: Dexie;
private poisDatabase: Dexie;
private uploadQueueDatabase: Dexie;
private imagesDatabase: Dexie;
private shareUrlsDatabase: Dexie;
private tracesDatabase: Dexie;
Expand All @@ -52,11 +50,8 @@ export class DatabaseService {
this.stateDatabase.version(1).stores({
state: "id"
});
this.poisDatabase = new Dexie(DatabaseService.POIS_DB_NAME);
this.poisDatabase.version(1).stores({
pois: DatabaseService.POIS_ID_COLUMN + "," + DatabaseService.POIS_LOCATION_COLUMN,
});
this.poisDatabase.version(2).stores({
this.uploadQueueDatabase = new Dexie(DatabaseService.POIS_UPLOAD_QUEUE_DB_NAME);
this.uploadQueueDatabase.version(1).stores({
uploadQueue: DatabaseService.POIS_ID_COLUMN
});
this.imagesDatabase = new Dexie(DatabaseService.IMAGES_DB_NAME);
Expand Down Expand Up @@ -127,55 +122,16 @@ export class DatabaseService {
}
}

public storePois(pois: GeoJSON.Feature[]): Promise<any> {
return this.poisDatabase.table(DatabaseService.POIS_TABLE_NAME).bulkPut(pois);
}

public deletePois(poiIds: string[]): Promise<void> {
return this.poisDatabase.table(DatabaseService.POIS_TABLE_NAME).bulkDelete(poiIds);
}

public async getPoisForClustering(): Promise<GeoJSON.Feature<GeoJSON.Point>[]> {
this.loggingService.debug("[Database] Startting getting pois for clustering in chunks");
let features = [] as GeoJSON.Feature<GeoJSON.Point>[];
let index = 0;
const size = 2000;
let currentFeatures = [];
do {
currentFeatures = await this.poisDatabase.table(DatabaseService.POIS_TABLE_NAME).offset(index * size).limit(size).toArray();
features = features.concat(currentFeatures);
index++;
} while (currentFeatures.length !== 0);
this.loggingService.debug("[Database] Finished getting pois for clustering in chunks: " + features.length);
const pointFeatures = features.map((feature: GeoJSON.Feature) => {
const geoLocation = feature.properties.poiGeolocation;
const pointFeature = {
type: "Feature",
geometry: {
type: "Point",
coordinates: [parseFloat(geoLocation.lon), parseFloat(geoLocation.lat)]
},
properties: feature.properties
} as GeoJSON.Feature<GeoJSON.Point>;
return pointFeature;
});
return pointFeatures;
}

public getPoiById(id: string): Promise<GeoJSON.Feature> {
return this.poisDatabase.table(DatabaseService.POIS_TABLE_NAME).get(id);
}

public addPoiToUploadQueue(feature: GeoJSON.Feature): Promise<any> {
return this.poisDatabase.table(DatabaseService.POIS_UPLOAD_QUEUE_TABLE_NAME).put(feature);
return this.uploadQueueDatabase.table(DatabaseService.POIS_UPLOAD_QUEUE_TABLE_NAME).put(feature);
}

public getPoiFromUploadQueue(featureId: string): Promise<GeoJSON.Feature> {
return this.poisDatabase.table(DatabaseService.POIS_UPLOAD_QUEUE_TABLE_NAME).get(featureId);
return this.uploadQueueDatabase.table(DatabaseService.POIS_UPLOAD_QUEUE_TABLE_NAME).get(featureId);
}

public removePoiFromUploadQueue(featureId: string): Promise<void> {
return this.poisDatabase.table(DatabaseService.POIS_UPLOAD_QUEUE_TABLE_NAME).delete(featureId);
return this.uploadQueueDatabase.table(DatabaseService.POIS_UPLOAD_QUEUE_TABLE_NAME).delete(featureId);
}

public storeImages(images: ImageUrlAndData[]): Promise<any> {
Expand Down
Loading

0 comments on commit 16e8cfa

Please sign in to comment.