-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
279 additions
and
168 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
IsraelHiking.Web/src/application/services/geojson-utils.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { GeoJSONUtils } from "./geojson-utils"; | ||
|
||
describe("GeoJsonUtils", () => { | ||
it("should get extenal description for hebrew", () => { | ||
const results = GeoJSONUtils.getExternalDescription( | ||
{properties: { "poiExternalDescription:he": "desc"}} as any as GeoJSON.Feature, "he"); | ||
expect(results).toBe("desc"); | ||
}); | ||
|
||
it("should get extenal description for language independant", () => { | ||
const results = GeoJSONUtils.getExternalDescription( | ||
{properties: { poiExternalDescription: "desc"}} as any as GeoJSON.Feature, "he"); | ||
expect(results).toBe("desc"); | ||
}); | ||
|
||
it("should get title when there's mtb name with language", () => { | ||
const results = GeoJSONUtils.getTitle({properties: { "mtb:name:he": "name"}} as any as GeoJSON.Feature, "he"); | ||
expect(results).toBe("name"); | ||
}); | ||
|
||
it("should get title when there's mtb name without language", () => { | ||
const results = GeoJSONUtils.getTitle({properties: { "mtb:name": "name"}} as any as GeoJSON.Feature, "he"); | ||
expect(results).toBe("name"); | ||
}); | ||
|
||
it("should get title even when there's no title for language description", () => { | ||
const results = GeoJSONUtils.getTitle({properties: { name: "name"}} as any as GeoJSON.Feature, "he"); | ||
expect(results).toBe("name"); | ||
}); | ||
|
||
it("should get title even when there's no title for language description", () => { | ||
const results = GeoJSONUtils.getTitle({properties: { name: "name"}} as any as GeoJSON.Feature, "he"); | ||
expect(results).toBe("name"); | ||
}); | ||
it("should return has extra data for feature with description", () => { | ||
expect(GeoJSONUtils.hasExtraData({properties: { "description:he": "desc"}} as any as GeoJSON.Feature, "he")).toBeTruthy(); | ||
}); | ||
|
||
it("should return has extra data for feature with image", () => { | ||
expect(GeoJSONUtils.hasExtraData({properties: { image: "image-url"}} as any as GeoJSON.Feature, "he")).toBeTruthy(); | ||
}); | ||
}); |
71 changes: 71 additions & 0 deletions
71
IsraelHiking.Web/src/application/services/geojson-utils.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { Immutable } from "immer"; | ||
import { LatLngAlt } from "../models/models"; | ||
|
||
export class GeoJSONUtils { | ||
public static setProperty(feature: GeoJSON.Feature, key: string, value: string): string { | ||
if (!feature.properties[key]) { | ||
feature.properties[key] = value; | ||
return ""; | ||
} | ||
let index = 1; | ||
while (feature.properties[key + index]) { | ||
index++; | ||
} | ||
feature.properties[key + index] = value; | ||
return `${index}`; | ||
} | ||
|
||
public static setLocation(feature: GeoJSON.Feature, value: LatLngAlt) { | ||
feature.properties.poiGeolocation = { | ||
lat: value.lat, | ||
lon: value.lng | ||
}; | ||
} | ||
|
||
public static setDescription(feature: GeoJSON.Feature, value: string, language: string) { | ||
feature.properties["description:" + language] = value; | ||
} | ||
|
||
public static setTitle(feature: GeoJSON.Feature, value: string, language: string) { | ||
feature.properties["name:" + language] = value; | ||
} | ||
|
||
public static getTitle(feature: Immutable<GeoJSON.Feature>, language: string): string { | ||
if (feature.properties["name:" + language]) { | ||
return feature.properties["name:" + language]; | ||
} | ||
if (feature.properties.name) { | ||
return feature.properties.name; | ||
} | ||
if (feature.properties["mtb:name:"+ language]) { | ||
return feature.properties["mtb:name:"+ language]; | ||
} | ||
if (feature.properties["mtb:name"]) { | ||
return feature.properties["mtb:name"]; | ||
} | ||
return ""; | ||
} | ||
|
||
public static getDescription(feature: Immutable<GeoJSON.Feature>, language: string): string { | ||
return feature.properties["description:" + language] || feature.properties.description; | ||
} | ||
|
||
public static getExternalDescription(feature: GeoJSON.Feature, language: string): string { | ||
return feature.properties["poiExternalDescription:" + language] || feature.properties.poiExternalDescription; | ||
} | ||
|
||
public static getLocation(feature: GeoJSON.Feature): LatLngAlt { | ||
return { | ||
lat: feature.properties.poiGeolocation.lat, | ||
lng: feature.properties.poiGeolocation.lon, | ||
alt: feature.properties.poiAlt | ||
}; | ||
} | ||
|
||
public static hasExtraData(feature: GeoJSON.Feature, language: string): boolean { | ||
return feature.properties["description:" + language] || | ||
Object.keys(feature.properties).find(k => k.startsWith("image")) != null || | ||
Object.keys(feature.properties).find(k => k.startsWith("wikipedia")) != null || | ||
Object.keys(feature.properties).find(k => k.startsWith("wikidata")) != null; | ||
} | ||
} |
102 changes: 102 additions & 0 deletions
102
IsraelHiking.Web/src/application/services/iNature.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { HttpClient } from "@angular/common/http"; | ||
import { Injectable } from "@angular/core"; | ||
import { firstValueFrom, timeout } from "rxjs"; | ||
|
||
import { GeoJSONUtils } from "./geojson-utils"; | ||
import { Urls } from "../urls"; | ||
|
||
@Injectable() | ||
export class INatureService { | ||
private readonly API_URL = "https://inature.info/w/api.php"; | ||
private readonly TIMEOUT = 3000; | ||
|
||
constructor(private readonly httpClient: HttpClient) {} | ||
|
||
public async createFeatureFromPageId(pageId: string): Promise<GeoJSON.Feature> { | ||
const address = this.getContnetRetrivalAddress(pageId, true); | ||
const {content, title} = await this.getPageContentAndTitleFromAddress(address); | ||
const feature: GeoJSON.Feature = { | ||
type: "Feature", | ||
properties: {}, | ||
geometry: { | ||
type: "Point", | ||
coordinates: [] | ||
} | ||
}; | ||
const lngLat = this.setLocation(content, feature); | ||
this.setImageAndWebsite(content, feature, pageId); | ||
feature.geometry = await this.getGeometryFromContent(content) ?? { | ||
type: "Point", | ||
coordinates: [lngLat.lng, lngLat.lat] | ||
}; | ||
feature.properties.poiSource = "iNature"; | ||
feature.properties.poiCategory = "iNature"; | ||
feature.properties.poiLanguage = "he"; | ||
feature.properties.poiId = "iNature_" + pageId; | ||
feature.properties.identifier = pageId; | ||
feature.properties.name = title; | ||
if (feature.geometry.type === "LineString") { | ||
feature.properties.icon = "icon-hiking"; | ||
feature.properties.poiCategory = "Hiking"; | ||
feature.properties.iconColor = "black"; | ||
feature.properties.name = feature.properties.name + " - טבע ונופים"; | ||
} else { | ||
feature.properties.icon = "icon-nature"; | ||
feature.properties.poiCategory = "iNature"; | ||
feature.properties.iconColor = "#116C00"; | ||
} | ||
return feature; | ||
} | ||
|
||
public async enritchFeatureFromINature(feature: GeoJSON.Feature): Promise<void> { | ||
const iNatureRef = feature.properties["ref:IL:inature"]; | ||
const address = this.getContnetRetrivalAddress(iNatureRef, false); | ||
const contentAndTitle = await this.getPageContentAndTitleFromAddress(address); | ||
this.setImageAndWebsite(contentAndTitle.content, feature, iNatureRef); | ||
} | ||
|
||
private setLocation(content: string, feature: GeoJSON.Feature) { | ||
const regexp = /נצ=(\d+\.\d+)\s*,\s*(\d+\.\d+)/; | ||
const match = content.match(regexp); | ||
const latLng = { lat: parseFloat(match[1]), lng: parseFloat(match[2]) }; | ||
GeoJSONUtils.setLocation(feature, latLng); | ||
return latLng; | ||
} | ||
|
||
private setImageAndWebsite(content: string, feature: GeoJSON.Feature, title: string) { | ||
feature.properties.poiExternalDescription = content.match(/סקירה=(.*)/)[1]; | ||
const indexString = GeoJSONUtils.setProperty(feature, "website", `https://inature.info/wiki/${title}`); | ||
feature.properties["poiSourceImageUrl" + indexString] = "https://user-images.githubusercontent.com/3269297/37312048-2d6e7488-2652-11e8-9dbe-c1465ff2e197.png"; | ||
const image = content.match(/תמונה=(.*)/)[1]; | ||
const imageSrc = `https://inature.info/w/index.php?title=Special:Redirect/file/${image}`; | ||
GeoJSONUtils.setProperty(feature, "image", imageSrc); | ||
} | ||
|
||
private getContnetRetrivalAddress(key: string, isPageId: boolean): string { | ||
const baseAddress = `${this.API_URL}?action=query&prop=revisions&rvprop=content&format=json&origin=*` | ||
if (isPageId) { | ||
return baseAddress + `&pageids=${key}`; | ||
} | ||
return baseAddress + `&titles=${key}`; | ||
} | ||
|
||
private async getPageContentAndTitleFromAddress(address: string): Promise<{content: string, title: string}> { | ||
const iNatureJson = await firstValueFrom(this.httpClient.get(address).pipe(timeout(this.TIMEOUT))) as any; | ||
const pageData = iNatureJson.query.pages[Object.keys(iNatureJson.query.pages)[0]]; | ||
return { | ||
content: pageData.revisions[0]["*"], | ||
title: pageData.title | ||
} | ||
} | ||
|
||
private async getGeometryFromContent(content: string): Promise<GeoJSON.Geometry> { | ||
const shareRegexp = new RegExp("israelhiking\.osm\.org\.il/share/(.*?)"); | ||
Check failure on line 93 in IsraelHiking.Web/src/application/services/iNature.service.ts
|
||
const match = content.match(shareRegexp); | ||
if (match == null) { | ||
return null; | ||
} | ||
const shareId = match[1]; | ||
const geojson = await firstValueFrom(this.httpClient.get(`${Urls.urls}${shareId}?format=geojson`)) as GeoJSON.FeatureCollection; | ||
return geojson.features.find(f => f.geometry.type === "LineString")?.geometry; | ||
} | ||
} |
Oops, something went wrong.