Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add terrain-profile to oskari-server-extras #18

Merged
merged 2 commits into from
Mar 25, 2022
Merged
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
32 changes: 32 additions & 0 deletions service-terrain-profile/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
### Terrain Profile

Terrain Profile Backend Service. You send a 2D GeoJSON LineString in EPSG:3067, I respond with a 3D MultiPoint. Each Point resides on your LineString and has the altitude as the third dimension value.

The logic:

1. Client ==> GeoJSON Feature/LineString ==> ActionHandler
2. ActionHandler ==> WCS Request ==> WCS Service
3. WCS Service ==> DEM / GeoTIFF ==> ActionHandler
4. ActionHandler ==> GeoJSON Feature/MultiPoint ==> Client

Requires following properties:

property | description
-------- | -----------
`terrain.profile.wcs.endPoint` | URL of the WCS service, query string is NOT ALLOWED (e.g. do NOT specify '?', otherwise it won't work)
`terrain.profile.wcs.demCoverageId`| id of the DEM coverage in the WCS service

Available parameters per request feature.properties.$key:

property | description
-------- | -----------
`numPoints` | Number of points you want back (default 100). If your LineString has more coordinates than this value, we will use that number. Maximum number of points is 1000 (even if your LineString has more coordinates than that).
`resolution` | *Ignored at the moment*. Used for describing the level-of-detail you're interested in.

Response properties in feature.properties.$key:

property | description
-------- | -----------
`numPoints` | Number of points.
`distanceFromStart` | Array of numbers, each describing the distance from the begin of the LineString. Numbers are ordered and evenly spaced, unless requested numPoints was less than number of coordinates in the requested LineString (see previous table)
`resolution` | *Ignored at the moment*.
183 changes: 183 additions & 0 deletions service-terrain-profile/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>fi.nls.oskari.extras</groupId>
<artifactId>service-terrain-profile</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>Terrain Profile</name>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jdk.version>1.8</jdk.version>
<oskari.version>[2.7.0,)</oskari.version>
<hystrix.version>1.5.18</hystrix.version>
<geotools.version>25.1</geotools.version>
<junit.version>4.13</junit.version>
<geojson-jackson.version>1.14</geojson-jackson.version>
</properties>

<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.oskari</groupId>
<artifactId>service-control</artifactId>
<version>${oskari.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.oskari</groupId>
<artifactId>service-wcs</artifactId>
<version>${oskari.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>${hystrix.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-opengis</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-referencing</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.oskari</groupId>
<artifactId>shared-test-resources</artifactId>
<version>${oskari.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.grundid.opendatalab</groupId>
<artifactId>geojson-jackson</artifactId>
<version>${geojson-jackson.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>deploy</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<failOnError>false</failOnError>
</configuration>
<executions>
<execution>
<id>attach-sources</id>
<phase>deploy</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<!-- explicitly define maven-deploy-plugin after other to
force exec order -->
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
<executions>
<execution>
<id>deploy</id>
<phase>deploy</phase>
<goals>
<goal>deploy</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

<repositories>
<repository>
<id>oskari_org</id>
<name>Oskari.org repository</name>
<url>https://oskari.org/repository/maven/releases/</url>
</repository>
<repository>
<id>oskari_org_snapshot</id>
<name>Oskari.org snapshot repository</name>
<url>https://oskari.org/repository/maven/snapshots/</url>
</repository>
</repositories>

<distributionManagement>
<repository>
<id>oskari_org</id>
<name>Oskari.org repository</name>
<url>https://oskari.org/repository/maven/releases/</url>
</repository>
<snapshotRepository>
<id>oskari_org_snapshot</id>
<name>Oskari.org snapshot repository</name>
<url>https://oskari.org/repository/maven/snapshots/</url>
</snapshotRepository>
</distributionManagement>

<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package fi.nls.oskari.terrainprofile;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.concurrent.TimeoutException;

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixThreadPoolProperties;

import fi.nls.oskari.util.IOHelper;
import fi.nls.oskari.util.PropertyUtil;

/**
* HystrixCommand that sends a GetCoverage request to the WCS service via HTTP
* and reads the response to a byte array.
*
* In case the service responds with a 504 status code this
* code sleeps for a bit and then tries again up to MAX_RETRIES times
*/
public class CommandGetCoverage extends HystrixCommand<byte[]> {

private static final String GROUP_KEY = "oskari.terrainprofile";
private static final String COMMAND_NAME = "getCoverage";
private static final int MAX_RETRIES = 5;
private static final int SLEEP_BETWEEN_RETRY_MS = 100;

private final String request;

public CommandGetCoverage(String request) {
super(Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(GROUP_KEY))
.andCommandKey(HystrixCommandKey.Factory.asKey(COMMAND_NAME))
.andThreadPoolPropertiesDefaults(
HystrixThreadPoolProperties.Setter()
.withCoreSize(PropertyUtil.getOptional(GROUP_KEY + ".job.pool.size", 4))
.withMaxQueueSize(PropertyUtil.getOptional(GROUP_KEY + ".job.pool.limit", 100))
.withQueueSizeRejectionThreshold(PropertyUtil.getOptional(GROUP_KEY + ".job.pool.queue", 100)))
.andCommandPropertiesDefaults(
HystrixCommandProperties.Setter()
.withExecutionTimeoutInMilliseconds(PropertyUtil.getOptional(GROUP_KEY + ".job.timeoutms", 15000))
.withCircuitBreakerRequestVolumeThreshold(PropertyUtil.getOptional(GROUP_KEY + ".failrequests", 10))
.withCircuitBreakerSleepWindowInMilliseconds(PropertyUtil.getOptional(GROUP_KEY + ".sleepwindow", 10000)))
);
this.request = request;
}

@Override
protected byte[] run() throws TimeoutException, IOException, Exception {
HttpURLConnection conn = IOHelper.getConnection(request);
int sc = conn.getResponseCode();

int tryCounter = 0;
while (sc == HttpURLConnection.HTTP_GATEWAY_TIMEOUT) {
if (++tryCounter == MAX_RETRIES) {
throw new TimeoutException();
}
Thread.sleep(SLEEP_BETWEEN_RETRY_MS);
conn = IOHelper.getConnection(request);
sc = conn.getResponseCode();
}

if (sc == HttpURLConnection.HTTP_OK) {
return IOHelper.readBytes(conn);
}

throw new Exception("Unexpected response to GetCoverage");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package fi.nls.oskari.terrainprofile;

public class DataPoint {

private double e;
private double n;
private double altitude;
private double distFromStart;
private int gridX;
private int gridY;
private int tileX;
private int tileY;

public double getE() {
return e;
}

public void setE(double e) {
this.e = e;
}

public double getN() {
return n;
}

public void setN(double n) {
this.n = n;
}

public double getAltitude() {
return altitude;
}

public void setAltitude(double altitude) {
this.altitude = altitude;
}

public double getDistFromStart() {
return distFromStart;
}

public void setDistFromStart(double distFromStart) {
this.distFromStart = distFromStart;
}

public int getGridX() {
return gridX;
}

public void setGridX(int gridX) {
this.gridX = gridX;
}

public int getGridY() {
return gridY;
}

public void setGridY(int gridY) {
this.gridY = gridY;
}

public int getTileX() {
return tileX;
}

public void setTileX(int tileX) {
this.tileX = tileX;
}

public int getTileY() {
return tileY;
}

public void setTileY(int tileY) {
this.tileY = tileY;
}

}
Loading