Skip to content

Commit

Permalink
Add wikidata service to present wikidata point online.
Browse files Browse the repository at this point in the history
  • Loading branch information
HarelM committed Sep 4, 2024
1 parent 5cc36d7 commit 1ca49aa
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 31 deletions.
2 changes: 2 additions & 0 deletions IsraelHiking.Web/src/application/application.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ 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";
// interactions
import { RouteEditPoiInteraction } from "./components/intercations/route-edit-poi.interaction";
import { RouteEditRouteInteraction } from "./components/intercations/route-edit-route.interaction";
Expand Down Expand Up @@ -311,6 +312,7 @@ const initializeApplication = (injector: Injector) => async () => {
PmTilesService,
ApplicationUpdateService,
INatureService,
WikidataService,
AudioPlayerFactory,
FileSystemWrapper,
// eslint-disable-next-line
Expand Down
10 changes: 5 additions & 5 deletions IsraelHiking.Web/src/application/services/inature.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ export class INatureService {
feature.properties.poiId = "iNature_" + pageId;
feature.properties.identifier = pageId;
feature.properties.name = title;
if (feature.geometry.type === "LineString") {
feature.properties.icon = "icon-hiking";
if (feature.geometry.type !== "Point") {
feature.properties.poiIcon = "icon-hike";
feature.properties.poiCategory = "Hiking";
feature.properties.iconColor = "black";
feature.properties.poiIconColor = "black";
feature.properties.name = feature.properties.name + " - טבע ונופים";
} else {
feature.properties.icon = "icon-nature";
feature.properties.poiIcon = "icon-inature";
feature.properties.poiCategory = "iNature";
feature.properties.iconColor = "#116C00";
feature.properties.poiIconColor = "#116C00";
}
return feature;
}
Expand Down
6 changes: 4 additions & 2 deletions IsraelHiking.Web/src/application/services/poi.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ import { ToastService } from "./toast.service";
import { MapService } from "./map.service";
import { ConnectionService } from "./connection.service";
import { OverpassTurboService } from "./overpass-turbo.service";
import { INatureService } from "./inature.service";
import { WikidataService } from "./wikidata.service";
import { GeoJSONUtils } from "./geojson-utils";
import { GeoJsonParser } from "./geojson.parser";
import { Urls } from "../urls";
import { LayersReducer } from "../reducers/layers.reducer";
import { AddToPoiQueueAction, OfflineReducer } from "../reducers/offline.reducer";
import { ConfigurationReducer, SetLanguageAction } from "../reducers/configuration.reducer";
import { GeoJSONUtils } from "./geojson-utils";
import { INatureService } from "./inature.service";
import type { Category, MarkerData } from "../models/models";

describe("Poi Service", () => {
Expand Down Expand Up @@ -78,6 +79,7 @@ describe("Poi Service", () => {
{ provide: LoggingService, useValue: loggingService },
{ provide: OverpassTurboService, useValue: {} },
{ provide: INatureService, useValue: {} },
{ provide: WikidataService, useValue: {} },
ConnectionService,
GeoJsonParser,
RunningContextService,
Expand Down
34 changes: 10 additions & 24 deletions IsraelHiking.Web/src/application/services/poi.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import { GeoJsonParser } from "./geojson.parser";
import { MapService } from "./map.service";
import { ConnectionService } from "./connection.service";
import { OverpassTurboService } from "./overpass-turbo.service";
import { GeoJSONUtils } from "./geojson-utils";
import { INatureService } from "./inature.service";
import { WikidataService } from "./wikidata.service";
import { AddToPoiQueueAction, RemoveFromPoiQueueAction } from "../reducers/offline.reducer";
import {
SetCategoriesGroupVisibilityAction,
Expand All @@ -39,8 +42,6 @@ import type {
EditablePublicPointData,
OfflineState
} from "../models/models";
import { GeoJSONUtils } from "./geojson-utils";
import { INatureService } from "./inature.service";

export type SimplePointType = "Tap" | "CattleGrid" | "Parking" | "OpenGate" | "ClosedGate" | "Block" | "PicnicSite"

Expand Down Expand Up @@ -123,6 +124,7 @@ export class PoiService {
private readonly connectionService: ConnectionService,
private readonly overpassTurboService: OverpassTurboService,
private readonly iNatureService: INatureService,
private readonly wikidataService: WikidataService,
private readonly store: Store
) {
this.poisCache = [];
Expand Down Expand Up @@ -655,24 +657,7 @@ export class PoiService {
return selectableCategories;
}

private async enritchFeatureFromWikimedia(feature: GeoJSON.Feature, language: string): Promise<void> {
const url = `https://www.wikidata.org/w/rest.php/wikibase/v0/entities/items/${feature.properties.wikidata}`;
const wikidata = await firstValueFrom(this.httpClient.get(url).pipe(timeout(3000))) as any;
const languageShort = language || this.resources.getCurrentLanguageCodeSimplified();
const title = wikidata.sitelinks[`${languageShort}wiki`]?.title;
if (wikidata.statements.P18 && wikidata.statements.P18.length > 0) {
GeoJSONUtils.setProperty(feature, "image", `File:${wikidata.statements.P18[0].value.content}`);
}
if (title) {
const indexString = GeoJSONUtils.setProperty(feature, "website", `https://${languageShort}.wikipedia.org/wiki/${title}`);
feature.properties["poiSourceImageUrl" + indexString] = "https://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg/128px-Wikipedia-logo-v2.svg.png";
}
const wikipediaPage = await firstValueFrom(this.httpClient.get(`http://${languageShort}.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&exintro=&explaintext=&titles=${title}&origin=*`)) as any;
const pagesIds = Object.keys(wikipediaPage.query.pages);
if (pagesIds.length > 0) {
feature.properties.poiExternalDescription = wikipediaPage.query.pages[pagesIds[0]].extract;
}
}


public async getPoint(id: string, source: string, language?: string): Promise<GeoJSON.Feature> {
const itemInCache = this.poisCache.find(f => this.getFeatureId(f) === id && f.properties.source === source);
Expand All @@ -694,7 +679,7 @@ export class PoiService {
let placePromise = Promise.resolve({features: []});
let wayPromise = Promise.resolve({features: []});
if (feature.properties.wikidata) {
wikidataPromise = this.enritchFeatureFromWikimedia(feature, language);
wikidataPromise = this.wikidataService.enritchFeatureFromWikimedia(feature, language);
}
if (feature.properties["ref:IL:inature"] && language === "he") {
inaturePromise = this.iNatureService.enritchFeatureFromINature(feature);
Expand Down Expand Up @@ -725,9 +710,10 @@ export class PoiService {
const feature = await this.iNatureService.createFeatureFromPageId(id);
this.poisCache.splice(0, 0, feature);
return cloneDeep(feature);
// HM TODO: add support for wikidata
//} else if (source === "wikidata") {

} else if (source === "Wikidata") {
const feature = await this.wikidataService.createFeatureFromPageId(id, language);
this.poisCache.splice(0, 0, feature);
return cloneDeep(feature);
} else {
const params = new HttpParams().set("language", language || this.resources.getCurrentLanguageCodeSimplified());
const poi$ = this.httpClient.get(Urls.poi + source + "/" + id, { params }).pipe(timeout(6000));
Expand Down
88 changes: 88 additions & 0 deletions IsraelHiking.Web/src/application/services/wikidata.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";

import { ResourcesService } from "./resources.service";
import { firstValueFrom, timeout } from "rxjs";
import { GeoJSONUtils } from "./geojson-utils";

type WikiDataPage = {
sitelinks: { [key: string]: { site: string, title: string } };
statements: { [key: string]: { value: { content: any } }[] };
}

@Injectable()
export class WikidataService {

constructor(private readonly httpClient: HttpClient,
private readonly resources: ResourcesService) {
}

public async enritchFeatureFromWikimedia(feature: GeoJSON.Feature, language: string): Promise<void> {
const languageShort = language || this.resources.getCurrentLanguageCodeSimplified();
const wikidata = await this.getWikidataFromId(feature.properties.wikidata);
await this.setDescriptionAndImages(wikidata, feature, languageShort);
}

public async createFeatureFromPageId(wikidataId: string, language: string): Promise<GeoJSON.Feature> {
const wikidata = await this.getWikidataFromId(wikidataId);

const feature: GeoJSON.Feature<GeoJSON.Point> = {
type: "Feature",
properties: {
wikidata: wikidataId,
poiSource: "Wikidata",
poiId: "Wikidata_" + wikidataId,
poiIcon: "icon-wikipedia-w",
poiCategory: "Wikipedia",
poiIconColor: "black",
poiSourceImageUrl: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6a/Wikidata-logo.svg/128px-Wikidata-logo.svg.png",
poiLanguage: language
},
geometry: {
type: "Point",
coordinates: []
}
};
await this.enritchFeatureFromWikimedia(feature, language);
const lngLat = this.setLocation(wikidata, feature);
feature.geometry.coordinates = [lngLat.lng, lngLat.lat];
feature.properties.name = this.getTitle(wikidata, language);
return feature;
}

private async getWikidataFromId(wikidataId: string): Promise<WikiDataPage> {
const url = `https://www.wikidata.org/w/rest.php/wikibase/v0/entities/items/${wikidataId}`;
return await firstValueFrom(this.httpClient.get(url).pipe(timeout(3000))) as any;
}

private async setDescriptionAndImages(wikidata: WikiDataPage, feature: GeoJSON.Feature, language: string): Promise<void> {
if (wikidata.statements.P18 && wikidata.statements.P18.length > 0) {
GeoJSONUtils.setProperty(feature, "image", `File:${wikidata.statements.P18[0].value.content}`);
}
const title = this.getTitle(wikidata, language);
if (title) {
const indexString = GeoJSONUtils.setProperty(feature, "website", `https://${language}.wikipedia.org/wiki/${title}`);
feature.properties["poiSourceImageUrl" + indexString] = "https://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg/128px-Wikipedia-logo-v2.svg.png";
}
const wikipediaPage = await firstValueFrom(this.httpClient.get(`http://${language}.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&exintro=&explaintext=&titles=${title}&origin=*`)) as any;
const pagesIds = Object.keys(wikipediaPage.query.pages);
if (pagesIds.length > 0) {
feature.properties.poiExternalDescription = wikipediaPage.query.pages[pagesIds[0]].extract;
}
}

private setLocation(wikidata: WikiDataPage, feature: GeoJSON.Feature) {
const latLng = { lat: 0, lng: 0 };
if (wikidata.statements.P625 && wikidata.statements.P625.length > 0) {
const coordinates = wikidata.statements.P625[0].value.content;
latLng.lat = coordinates.latitude;
latLng.lng = coordinates.longitude;
}
GeoJSONUtils.setLocation(feature, latLng);
return latLng;
}

private getTitle(wikidata: WikiDataPage, language: string): string {
return wikidata.sitelinks[`${language}wiki`]?.title;
}
}

0 comments on commit 1ca49aa

Please sign in to comment.