Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/deploy-geo.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Build and Deploy AlpsLib-Geo

on:
workflow_dispatch:
push:
branches: [ main ]
paths:
- 'geo/build.gradle.kts'
# - 'geo/**'
# - '.github/workflows/deploy-geo.yml'
# - 'gradle/**'
# - 'buildSrc/**'

jobs:
build-and-deploy:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Set up JDK
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '21'
cache: 'gradle'

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v5

- name: Build with Gradle
run: ./gradlew clean :alpslib-geo:build

# The USERNAME and TOKEN need to correspond to the credentials environment variables used in
# the publishing section of your build.gradle
- name: Publish to GitHub Packages
run: ./gradlew :alpslib-geo:publish
env:
ORG_GRADLE_PROJECT_alpsMavenUser: ${{ secrets.MAVEN_USERNAME }}
ORG_GRADLE_PROJECT_alpsMavenPassword: ${{ secrets.MAVEN_PASSWORD }}
3 changes: 2 additions & 1 deletion .idea/gradle.xml

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

5 changes: 5 additions & 0 deletions .idea/jarRepositories.xml

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

22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Modules

You find the latest version of the AlpsLib modules on the AlpsBTE Maven Repository:
https://mvn.alps-bte.com/#browse/browse:alps-lib
https://mvn.alps-bte.com/#browse/browse:alps-bte:com%2Falpsbte%2Falpslib
Currently there is no wiki how to use the modules, but you can look up the source code of the modules.

Repository
Expand All @@ -18,7 +18,21 @@ Repository

Replace ```latest``` with the version of the module you want to use. You can find a list of all versions by clicking the link above.

### [AlpsLib-Hologram](https://mvn.alps-bte.com/#browse/browse:alps-lib:com/alpsbte/alpslib/alpslib-hologram)
### [AlpsLib-Geo](https://mvn.alps-bte.com/#browse/browse:alps-bte:com%2Falpsbte%2Falpslib%2Falpslib-geo)

This module can be used to get geo information (like country or city) of a location identified by latitude and longitude. This is possible both offline and over https using Nominatim.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please include a link to the readme of geocode

```xml
<dependencies>
<dependency>
<groupId>com.alpsbte.alpslib</groupId>
<artifactId>alpslib-geo</artifactId>
<version>latest</version>
</dependency>
</dependencies>
```

### [AlpsLib-Hologram](https://mvn.alps-bte.com/#browse/browse:alps-bte:com%2Falpsbte%2Falpslib%2Falpslib-hologram)

Includes an abstract HologramDisplay which can be used to create custom holograms.
We currently support DecentHolograms - look up the api version in th modules [pom.xml](./hologram/pom.xml).
Expand Down Expand Up @@ -51,7 +65,7 @@ Newer versions may work or may not work, depending on breaking api changes.
</dependencies>
```

### AlpsLib-IO
### [AlpsLib-IO](https://mvn.alps-bte.com/#browse/browse:alps-bte:com%2Falpsbte%2Falpslib%2Falpslib-io)

Includes Config-Manager, Language-Manager and Database-System.
The language system is replaced by [LangLibs](https://github.com/Cinnazeyy/LangLibs).
Expand Down Expand Up @@ -81,7 +95,7 @@ If you want tu use our database system, you also need to add the following depen
</dependencies>
```

### AlpsLib-Utils
### [AlpsLib-Utils](https://mvn.alps-bte.com/#browse/browse:alps-bte:com%2Falpsbte%2Falpslib%2Falpslib-utils)

Includes ItemBuilder & LoreBuilder, CustomHeads and other useful utilities.
```Important: This module needs to be included when using the AlpsLib-IO module.```
Expand Down
60 changes: 60 additions & 0 deletions geo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
This module can be used to get geo information (like country or city) of a location identified by latitude and longitude. This is possible both offline and over https using Nominatim.

# Retrieving data

## Offline ([ReverseGeocoder](https://github.com/kno10/reversegeocode))

First you need to create a RgcHandler. It utilizes the [ReverseGeocoder](https://github.com/kno10/reversegeocode) class by kno10. You need to provide an OSM data file (find them in [their repo](https://github.com/kno10/reversegeocode/tree/master/data)). The most accurate results can be achieved using files with high resolutions.

Using the RgcHandlers method "locationFromCoordinates" you can get a RgcGeoLocation with all information the ReverseGeocoder found.

Access it by using the "get" Method. You need to provide an AdminLevel (find more information below). This will use the AdminLevels Integer "level" property.

## [Nominatim](https://nominatim.org/release-docs/develop/api/Reverse/)

First you need to create a NominatimHandler. It utilizes the Nominatim reverse API to access OSM data. Because of that you need to provide your applications userAgent for the http requests.

Using the NominatimHandlers method "locationFromCoordinates" you can get a NominatimGeoLocation with all information of the "address" JSON object of the Nominatim response.

Access it by using the "get" Method. You need to provide an AdminLevel (find more information below). This will use the AdminLevels String "nominatimKey" property.

# AdminLevel

AdminLevels provide a constant way to access the parts of the queried data that you want to use. For example, you might want to define an AdminLevel for States in Germany:

```
public enum DEAdminLevel implements AdminLevel {

STATE(4);

private final int level;

DEAdminLevel(int level) {
this.level = level;
}

@Override
public Integer getLevel() {
return this.level;
}

@Override
public String getNominatimKey() {
return null;
}

}
```

For Rgc: An AdminLevels "level" refers to [OSM admin_levels](https://wiki.openstreetmap.org/wiki/Tag:boundary%3Dadministrative). As you can see, their meaning is not the same for every country.

For Nominatim: An AdminLevels "nominatimKey" refers to the keys used for the data in the "address" JSON response of a [Nominatim request](https://nominatim.org/release-docs/develop/api/Reverse/).

Both "level" and "nominatimKey" are allowed to be null as some AdminLevels only exist for RgcGeoLocations and others for NominationGeoLocations.

The "AdminLevel.COUNTRY" is the only one implemented by default as it is the same for all countries.

# Other peoples work used

- OSM (OpenStreetMap) data: © OpenStreetMap contributors
- [ReverseGeocoder](https://github.com/kno10/reversegeocode): Copyright (c) 2015, Erich Schubert
12 changes: 12 additions & 0 deletions geo/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
plugins {
id("buildlogic.java-conventions")
alias(libs.plugins.freefair.lombok)
}

dependencies {
compileOnly(libs.org.slf4j.slf4j.api)
compileOnly(libs.org.json.json)
}

description = "AlpsLib-Geo"
version = "1.0.0"
49 changes: 49 additions & 0 deletions geo/src/main/java/com/alpsbte/alpslib/geo/AdminLevel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.alpsbte.alpslib.geo;

/**
* Implementations represent either an OSM administrative level, a Nominatim address key or both for accessing parts of data, e.g. the country or city of a location
* @see <a href="https://wiki.openstreetmap.org/wiki/Tag:boundary%3Dadministrative">OSM administrative levels</a>
* @see <a href="https://nominatim.org/release-docs/develop/api/Reverse/">Nominatim documentation</a>
*/
public interface AdminLevel {

/**
* AdminLevel to provide access to country data. This is the only admin level that is the same for all countries.
*/
AdminLevel COUNTRY = AdminLevel.of(2, "country");

/**
* Get a simple AdminLevel without having to implement the interface manually
* @param level The numeric admin level if this AdminLevel is used for a {@link com.alpsbte.alpslib.geo.rgc.RgcGeoLocation}. null otherwise
* @param nominatimKey The key for the corresponding data to get from the Nominatim responses "address" JSON object if this AdminLevel is used for a {@link com.alpsbte.alpslib.geo.nominatim.NominatimGeoLocation}. null otherwise
* @return The AdminLevel with level and nominatimKey
*/
static AdminLevel of(Integer level, String nominatimKey) {
return new AdminLevel() {
@Override
public Integer getLevel() {
return level;
}

@Override
public String getNominatimKey() {
return nominatimKey;
}
};
}

/**
* The numeric admin level
* @return The numeric admin level or null if it is not set
* @see <a href="https://wiki.openstreetmap.org/wiki/Tag:boundary%3Dadministrative">OSM administrative levels</a>
*/
Integer getLevel();

/**
* The key for the corresponding data to get from the Nominatim responses "address" JSON object
* @return The key for the corresponding data to get from the Nominatim responses "address" JSON object
* @see <a href="https://nominatim.org/release-docs/develop/api/Reverse/">Nominatim documentation</a>
*/
String getNominatimKey();

}
15 changes: 15 additions & 0 deletions geo/src/main/java/com/alpsbte/alpslib/geo/GeoLocation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.alpsbte.alpslib.geo;

/**
* Holds data of a location that can be accessed using {@link AdminLevel}s.
*/
public interface GeoLocation {

/**
* Access a locations data value by an {@link AdminLevel}
* @param adminLevel Defines the data key
* @return The value for the key. Or null if the key is null or there is no value for the key in the data
*/
String get(AdminLevel adminLevel);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.alpsbte.alpslib.geo.nominatim;

import com.alpsbte.alpslib.geo.AdminLevel;
import com.alpsbte.alpslib.geo.GeoLocation;

import java.util.Map;

/**
* Represents a location with data from Nominatim rgc
* @param latitude The locations latitude
* @param longitude The locations longitude
* @param adminLevelsValues The Nominatim data with {@link AdminLevel#getNominatimKey()} values as keys
*/
public record NominatimGeoLocation(double latitude, double longitude, Map<String, String> adminLevelsValues) implements GeoLocation {

@Override
public String get(AdminLevel adminLevel) {
if (adminLevel.getNominatimKey() == null) {
return null;
}

return this.adminLevelsValues.get(adminLevel.getNominatimKey());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.alpsbte.alpslib.geo.nominatim;


import org.json.JSONObject;
import org.slf4j.Logger;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
* Provides access to retrieving location data from Nominatim (OSM).
* @see <a href="https://nominatim.org/release-docs/develop/api/Reverse/">Nominatim documentation</a>
*/
public class NominatimHandler {

private final Logger logger;
private final String userAgent;
private final int zoom;

/**
* Create a NominatimHandler object with a default zoom of 10
* @param logger The logger to use
* @param userAgent Your application's User Agent for the Nominatim http request.
*/
public NominatimHandler(Logger logger, String userAgent) {
this(logger, userAgent, 10);
}

/**
* Create a NominatimHandler object
* @param logger The logger to use
* @param userAgent Your application's User Agent for the Nominatim http request.
* @param zoom The zoom for the returned data's detail
*/
public NominatimHandler(Logger logger, String userAgent, int zoom) {
this.logger = logger;
this.userAgent = userAgent;
this.zoom = zoom;
}

/**
* Get location data for coordinates
* @param latitude The locations latitude
* @param longitude The locations longitude
* @return The location data or null if there were errors
*/
public NominatimGeoLocation locationFromCoordinates(float latitude, float longitude) {
try {
JSONObject response;

URL url = URI.create("https://nominatim.openstreetmap.org/reverse?lat=" + latitude + "&lon=" + longitude + "&format=json&zoom=" + this.zoom).toURL();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you make that komoot compatible? People might want to use that for obvious reasons

HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestProperty("User-Agent", this.userAgent);
con.setRequestProperty("Accept", "application/json");

BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8));
StringBuilder builder = new StringBuilder();
reader.lines().forEach(builder::append);
response = new JSONObject(builder.toString());
con.disconnect();

if (!response.has("address")) {
this.logger.warn("Invalid nominatim response: %s".formatted(response.toString()));
return null;
}

JSONObject address = response.getJSONObject("address");

Map<String, String> locValues = new HashMap<>();
for (String key : address.keySet()) {
locValues.put(key, address.getString(key));
}

return new NominatimGeoLocation(latitude, longitude, locValues);

} catch (IOException e) {
this.logger.error("Error nominatim", e);
return null;
}
}

}
Loading