├── .dockerignore
├── .gitignore
├── README.md
├── TanDEM-X 90
├── .gitignore
├── pom.xml
└── src
│ └── main
│ └── java
│ └── main
│ ├── DownloadTask.java
│ ├── Main.java
│ ├── ProgressDialog.java
│ ├── Start.java
│ └── ZipHandler.java
├── TanDEM90mDownloader.jar
├── _config.yml
├── create-dataset.sh
├── create-tiles.sh
├── data
└── TIFF-Files-here.md
├── docker-compose.yml
├── docker
└── Dockerfile
├── docs
└── api.md
├── download-srtm-data.sh
├── gdal_interfaces.py
├── gdal_interfaces.pyc
├── license.md
├── open-elevation.service
├── requirements.txt
├── server.py
└── swagger
└── swagger.json
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/.git
2 | data/
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | **/.idea/*
3 | *.iml
4 |
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Open-Elevation - Remake
2 | ### ↓ Read install instructions below ↓
3 |
4 | A free and open-source elevation API by Jorl17. The original is available at: [https://github.com/Jorl17/open-elevation](https://github.com/Jorl17/open-elevation). Thanks for your work.
5 |
6 |
7 | **Open-Elevation** is a free and open-source alternative to the [Google Elevation API](https://developers.google.com/maps/documentation/elevation/start) and similar offerings.
8 |
9 | This service came out of the need to have a hosted, easy to use and easy to setup elevation API. While there are some alternatives out there, none of them work out of the box, and seem to point to dead datasets. Open-Elevation is easy to setup, has its own docker image and provides scripts for you to easily acquire whatever datasets you want.
10 |
11 | Open-Elevation API Doc for details and ustage
12 | ##### [doc/api.md](https://github.com/Developer66/open-elevation/blob/master/docs/api.md)
13 |
14 |
15 | -----
16 | # Changes to the original version:
17 | * changing TIFF file locations - to working one - for SRTM 250m data in download script
18 | * changing create-dataset.sh to work again
19 | * fixing download scripts
20 |
21 | ### News:
22 | * added a preinstall documentation for dependencies
23 | * adding service file for linux (e.g. autostart)
24 |
25 | ### SRTM 90M from https://geoservice.dlr.de/
26 | * adding Doc for using SRTM 90m data from https://geoservice.dlr.de/web/dataguide/tdm90/
27 | * adding java downloader for data from https://geoservice.dlr.de/
28 | * adding JavaProject to download and xxtract all files and put it to the right place
29 |
30 | ### Bugs:
31 | * SRTM 90M TanDEM data are 40 to high. Issue: [here](https://github.com/Developer66/open-elevation/issues/1)
32 | -----
33 |
34 | # How to install:
35 | *I tested the install procedure on a fresh Ubuntu 18.10.*
36 |
37 |
38 | Fist of all clone this repository to your favourite location. (Use a permanent place for it where it won't be deleted)
39 |
40 | ### Default 250m files (after procedure ca. 20GB)
41 | 1. Make sure your system is up-to-date
42 |
43 | ```
44 | sudo apt-get update
45 | sudo apt-get upgrade -y
46 | ```
47 |
48 | 2. Install GDAL used for the GeoTIFFs
49 |
50 | ```
51 | sudo apt update
52 | sudo apt install gdal-bin python-gdal
53 |
54 | // Add libgal-dev
55 | sudo apt-get install libgdal-dev
56 |
57 | // Add unar
58 | sudo apt install unar
59 |
60 | // Create system vars
61 | export CPLUS_INCLUDE_PATH=/usr/include/gdal
62 | export C_INCLUDE_PATH=/usr/include/gdal
63 |
64 | sudo apt install python3-rtree
65 | ```
66 |
67 | 3. Install pip dependencies
68 |
69 | ```
70 | pip install -r requirements.txt
71 | ```
72 |
73 | 4. Download and progress GeoTIFFs
74 | ```
75 | ## open terminal and cd to your open-elevation dir ##
76 | // Mark scripts as executable
77 | sudo chmod +x download-srtm-data.sh create-tiles.sh create-dataset.sh
78 |
79 | // Execute
80 | ./create-dataset.sh
81 | ```
82 |
83 | The script should be downloading at this point and * this can take some time - up to 2 hours *.
84 |
85 | 5. **Optional** Adding Service to your computer (e.g. autostart)
86 | ```
87 | sudo mv <> /etc/systemd/system/open-elevation.service
88 |
89 | //Enable Autostart
90 | systemctl enable open-elevation
91 |
92 | //Following Commands can be used
93 | sudo service open-elevation start | stop | restart
94 | ```
95 |
96 | This service file (found in this repository at open-elevation.service) will also contain information to be specified manually such as the user and various pathways to the working directory.
97 |
98 | 6. Your server is now running reachable at 0.0.0.0:10000. **Congratulation**
99 |
100 | To change the ip edit the last line in **server.py**. You can choose ip and port whatever your want
101 |
102 | Test it:
103 | ```
104 | http://0.0.0.0:10000/api/v1/lookup?locations=48.179138,10.703618
105 | ```
106 |
107 | ### 90m TanDEM added myselfe (ca. 100GB)
108 |
109 | **Following step 1 to 3 from above**
110 |
111 | Than:
112 |
113 | 1. Install open-jre
114 | ```
115 | sudo apt-get install open-jre
116 | ```
117 |
118 | 2. Create an account at https://sso.eoc.dlr.de/cas/login
119 | 3. Generate a download the list of your needed locations at https://download.geoservice.dlr.de/TDM90/
120 |
121 | You can zoom out and use CTL to drag multiple rectangles
122 | 
123 |
124 | 4. Start the TanDEM90mDownloader.jar in your directory using this args:
125 | ```
126 | java -jar <> -i= -o= -u=
127 | *Optional number of Threads (default is 4)* -n=4
128 |
129 | Example:
130 | java -jar C:\TanDEM90mDownloader.jar -i=C:\urllist.txt -o=C:\data -u=max@mustermann.de -p=xyz1234
131 | ```
132 |
133 | The program than automatically download the zips, extract them and copy the DEM data to your output dir and deletes the zip after that to save storage.
134 |
135 | 5. **Optional** As 5. above
136 | 6. Your server is now running reachable at 0.0.0.0:10000. **Congratulation**
137 |
138 | To change the ip edit the last line in **server.py**. You can choose ip and port whatever your want
139 | Test it:
140 | ```
141 | http://0.0.0.0:10000/api/v1/lookup?locations=48.179138,10.703618
142 | ```
143 |
144 | # ! WARNING !
145 | Files from https://geoservice.dlr.de/ 30M are a little bit larger than the original files.
146 |
147 | *Infos from https://geoservice.dlr.de/web/dataguide/tdm90/ (2019.02.01).*
148 |
149 | | Key | Value
150 | | -------- | --------
151 | | Number of DEM products | 19389
152 | | Size of the global data set, zipped (including all annotations) | 253 GB
153 | | Size of the global data set, unzipped (including all annotations) | 534 GB
154 | | Size of all DEM raster files (unzipped, without annotations or meta data) | 93.8 GB
155 |
156 | -----
157 |
158 | # Install using docker (oringal not tested)
159 |
160 | You can freely host your own instance of Open-Elevation. There are two main options: Docker or native. We recommend using docker to ensure that your environment matches the development environment
161 |
162 | ## Clone the repository
163 | First things first, clone the repository and `cd` onto its directory
164 |
165 | ```
166 | git clone http://github.com/Jorl17/open-elevation
167 | cd open-elevation
168 | ```
169 |
170 |
171 | ## Using Docker
172 |
173 | An image of Open-Elevation is available at [DockerHub](https://hub.docker.com/r/openelevation/open-elevation/). You can use this image as the basis for your Open-Elevation installation.
174 |
175 | The Docker image roots itself at `/code/` and expects that all GeoTIFF datafiles be located at `/code/data/`, which you should mount using a volume.
176 |
177 | ### Prerequisites: Getting the dataset
178 |
179 | Open-Elevation doesn't come with any data of its own, but it offers a set of scripts to get the whole [SRTM 250m dataset](http://gisweb.ciat.cgiar.org/TRMM/SRTM_Resampled_250m/).
180 |
181 | #### Whole World
182 |
183 | If you wish to host the whole world, just run
184 |
185 | ```
186 | mkdir data # Create the target folder for the dataset
187 | docker run -t -i -v $(pwd)/data:/code/data openelevation/open-elevation /code/create-dataset.sh
188 | ```
189 |
190 | The above command should have downloaded the entire SRTM dataset and split it into multiple smaller files in the `data` directory. **Be aware that this directory may be over 20 GB in size after the process is completed!**
191 |
192 | #### Custom Data
193 |
194 | If you don't want to use the whole world, you can provide your own dataset in GeoTIFF format, compatible with the SRTM dataset. Simply drop the files for the regions you desire in the `data` directory. You are advised to split these files in smaller chunks so as to make Open-Elevation less memory-hungry (the largest file has to fit in memory). The `create-tiles.sh` is capable of doing this, and you can see it working in `create-dataset.sh`. Since you are using docker, you should always run the commands within the container. For example:
195 |
196 | ```
197 | docker run -t -i -v $(pwd)/data:/code/data openelevation/open-elevation /code/create-tiles.sh /code/data/SRTM_NE_250m.tif 10 10
198 | ```
199 |
200 | The above example command splits `SRTM_NE_250m.tif` into 10 by 10 files inside the `/code/data` directory, which is mapped to `$(pwd)/data`.
201 |
202 |
203 | ### Running the Server in Docker container
204 |
205 | Now that you've got your data, you're ready to run Open-Elevation! Simply run
206 |
207 | ```
208 | docker run -t -i -v $(pwd)/data:/code/data -p 8080:8080 openelevation/open-elevation
209 | ```
210 | Build image only:
211 | ```
212 | docker build . -f docker/Dockerfile
213 | ```
214 |
215 | This command:
216 |
217 | 1. Maps `$(pwd)/data` (your data directory) to `/code/data` within the container
218 | 2. Exposes port 8080 to forward to the container's port 8080
219 | 3. Runs the default command, which is the server at port 8080
220 |
221 | You should now be able to go to `https://localhost:8080` for all your open-route needs.
222 |
223 | ## Docker compose
224 | You can use `docker-compose.yml` to build image, create and run docker container
225 | ```
226 | docker-compose -f docker-compose.yml up -d
227 | ```
228 | server will be available on host port 8080:
229 | ```
230 | curl http://0.0.0.0:8080/api/v1/lookup?locations=42.216667,27.416667
231 | {"results": [{"latitude": 42.216667, "elevation": 262, "longitude": 27.416667}]}
232 | ```
233 |
234 | ## Generate API clients from swagger.json
235 | #### React Client
236 | We use [swagger-js-codegen](https://github.com/wcandillon/swagger-js-codegen)
237 | ```javascript
238 | const fs = require('fs');
239 | const {CodeGen} = require('swagger-js-codegen');
240 |
241 | const swaggerFile = 'swagger/swagger.json';
242 | const className = 'OpenElevationRestClient';
243 | const swagger = JSON.parse(fs.readFileSync(swaggerFile, 'UTF-8'));
244 |
245 | const elevationReactCode = CodeGen.getReactCode({
246 | moduleName: className,
247 | className,
248 | swagger,
249 | isES6: true,
250 | });
251 | save(elevationReactCode, className, '.js');
252 |
253 | function save(code, fileName, ext = '.js') {
254 | const outputDir = 'src/swagger/generated/';
255 | const outputFile = outputDir + fileName;
256 |
257 | if (!fs.existsSync(outputDir)) {
258 | fs.mkdirSync(outputDir);
259 | }
260 | fs.writeFileSync(outputFile + ext, '/* eslint-disable */\n/* This file is generated! Do not edit, your changes will be overridden */\n');
261 |
262 | fs.appendFileSync(outputFile + ext, code);
263 | }
264 | ```
265 | Example usage:
266 |
267 | ```javascript
268 |
269 | new OpenElevationRestClient().getLookup({locations: '42.216667,27.416667'}).then((results) => {
270 | console.log(results);
271 | return true;
272 | }).catch((error) => {
273 | console.log('Error getLookup:' + error);
274 | });
275 | new OpenElevationRestClient().postLookup({
276 | json: {
277 | locations: [{
278 | latitude: 42.216667,
279 | longitude: 27.416667
280 | }]
281 | }
282 | }).then((results) => {
283 | console.log(results);
284 | return true;
285 | }).catch((error) => {
286 | console.log('Error postLookup:' + error);
287 | });
288 | ```
289 |
290 |
291 | ## Problems
292 |
293 | Have you found any problems? Open an issue or submit your own pull request!
294 |
--------------------------------------------------------------------------------
/TanDEM-X 90/.gitignore:
--------------------------------------------------------------------------------
1 | ### Eclipse template
2 |
3 | .metadata
4 | bin/
5 | tmp/
6 | *.tmp
7 | *.bak
8 | *.swp
9 | *~.nib
10 | local.properties
11 | .settings/
12 | .loadpath
13 | .recommenders
14 |
15 | # External tool builders
16 | .externalToolBuilders/
17 |
18 | # Locally stored "Eclipse launch configurations"
19 | *.launch
20 |
21 | # PyDev specific (Python IDE for Eclipse)
22 | *.pydevproject
23 |
24 | # CDT-specific (C/C++ Development Tooling)
25 | .cproject
26 |
27 | # CDT- autotools
28 | .autotools
29 |
30 | # Java annotation processor (APT)
31 | .factorypath
32 |
33 | # PDT-specific (PHP Development Tools)
34 | .buildpath
35 |
36 | # sbteclipse plugin
37 | .target
38 |
39 | # Tern plugin
40 | .tern-project
41 |
42 | # TeXlipse plugin
43 | .texlipse
44 |
45 | # STS (Spring Tool Suite)
46 | .springBeans
47 |
48 | # Code Recommenders
49 | .recommenders/
50 |
51 | # Annotation Processing
52 | .apt_generated/
53 |
54 | # Scala IDE specific (Scala & Java development for Eclipse)
55 | .cache-main
56 | .scala_dependencies
57 | .worksheet
58 | ### JetBrains template
59 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
60 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
61 |
62 | # User-specific stuff
63 | .idea/**/workspace.xml
64 | .idea/**/tasks.xml
65 | .idea/**/usage.statistics.xml
66 | .idea/**/dictionaries
67 | .idea/**/shelf
68 |
69 | # Sensitive or high-churn files
70 | .idea/**/dataSources/
71 | .idea/**/dataSources.ids
72 | .idea/**/dataSources.local.xml
73 | .idea/**/sqlDataSources.xml
74 | .idea/**/dynamic.xml
75 | .idea/**/uiDesigner.xml
76 | .idea/**/dbnavigator.xml
77 |
78 | # Gradle
79 | .idea/**/gradle.xml
80 | .idea/**/libraries
81 |
82 | # Gradle and Maven with auto-import
83 | # When using Gradle or Maven with auto-import, you should exclude module files,
84 | # since they will be recreated, and may cause churn. Uncomment if using
85 | # auto-import.
86 | # .idea/modules.xml
87 | # .idea/*.iml
88 | # .idea/modules
89 |
90 | # CMake
91 | cmake-build-*/
92 |
93 | # Mongo Explorer plugin
94 | .idea/**/mongoSettings.xml
95 |
96 | # File-based project format
97 | *.iws
98 |
99 | # IntelliJ
100 | out/
101 |
102 | # mpeltonen/sbt-idea plugin
103 | .idea_modules/
104 |
105 | # JIRA plugin
106 | atlassian-ide-plugin.xml
107 |
108 | # Cursive Clojure plugin
109 | .idea/replstate.xml
110 |
111 | # Crashlytics plugin (for Android Studio and IntelliJ)
112 | com_crashlytics_export_strings.xml
113 | crashlytics.properties
114 | crashlytics-build.properties
115 | fabric.properties
116 |
117 | # Editor-based Rest Client
118 | .idea/httpRequests
119 | ### Maven template
120 | target/
121 | pom.xml.tag
122 | pom.xml.releaseBackup
123 | pom.xml.versionsBackup
124 | pom.xml.next
125 | release.properties
126 | dependency-reduced-pom.xml
127 | buildNumber.properties
128 | .mvn/timing.properties
129 | .mvn/wrapper/maven-wrapper.jar
130 |
--------------------------------------------------------------------------------
/TanDEM-X 90/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | TanDEM-X-90-Downloader
5 | TanDEM-X-90-Downloader
6 | 1.0-STABLE
7 |
8 |
9 |
10 |
11 |
12 | commons-cli
13 | commons-cli
14 | 1.4
15 |
16 |
17 |
18 | commons-io
19 | commons-io
20 | 2.6
21 |
22 |
23 |
24 | net.lingala.zip4j
25 | zip4j
26 | 1.3.2
27 |
28 |
29 |
30 | me.tongfei
31 | progressbar
32 | 0.7.1
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | org.apache.maven.plugins
41 | maven-compiler-plugin
42 | 3.7.0
43 |
44 | 1.8
45 | 1.8
46 |
47 |
48 |
49 |
50 |
51 |
52 | org.apache.maven.plugins
53 | maven-compiler-plugin
54 |
55 |
56 | maven-resources-plugin
57 | 2.6
58 |
59 |
60 | copy-resources
61 | validate
62 |
63 | copy-resources
64 |
65 |
66 | ${basedir}/target/Crunchify
67 |
68 |
69 | resources
70 | true
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | org.apache.maven.plugins
79 | maven-dependency-plugin
80 |
81 |
82 | copy-dependencies
83 | prepare-package
84 |
85 | copy-dependencies
86 |
87 |
88 | ${project.build.directory}/Crunchify/lib
89 | false
90 | false
91 | true
92 |
93 |
94 |
95 |
96 |
97 | org.apache.maven.plugins
98 | maven-jar-plugin
99 |
100 |
101 |
102 | true
103 | lib/
104 | com.crunchify.tutorial.CrunchifyMain
105 |
106 |
107 | .
108 |
109 |
110 |
111 | Crunchify/Crunchify
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/TanDEM-X 90/src/main/java/main/DownloadTask.java:
--------------------------------------------------------------------------------
1 | package main;
2 |
3 | import java.io.File;
4 | import java.io.FileOutputStream;
5 | import java.io.InputStream;
6 | import java.net.HttpURLConnection;
7 | import java.net.URL;
8 | import java.util.Base64;
9 |
10 | import org.apache.commons.io.FilenameUtils;
11 |
12 | /**
13 | * Download Task
14 | *
15 | * Downloading the zip file and start the decompress thread
16 | *
17 | * @author crypto
18 | *
19 | */
20 | public class DownloadTask implements Runnable {
21 |
22 | private final String inputList;
23 | private final File outputDir;
24 | private final String username;
25 | private final String passwd;
26 |
27 | /**
28 | * @param line
29 | * @param outputDir
30 | * @param username
31 | * @param passwd
32 | */
33 | public DownloadTask(String line, File outputDir, String username, String passwd) {
34 | this.inputList = line;
35 | this.outputDir = outputDir;
36 | this.username = username;
37 | this.passwd = passwd;
38 | }
39 |
40 | @Override
41 | public void run() {
42 | downloadFile(inputList);
43 | }
44 |
45 | /**
46 | * Download the file from param and starte the ZipHandler as an thread
47 | *
48 | * @param webUrl
49 | */
50 | public void downloadFile(String webUrl) {
51 | InputStream inputStream = null;
52 | FileOutputStream outputStream = null;
53 | HttpURLConnection urlConnection = null;
54 | String saveFilePath = null;
55 |
56 | try {
57 | URL url = new URL(webUrl);
58 | urlConnection = (HttpURLConnection) url.openConnection();
59 | urlConnection.setRequestMethod("GET");
60 |
61 | // generate savename
62 | saveFilePath = outputDir + File.separator + FilenameUtils.getName(url.getPath());
63 |
64 | // Building username and pass for login
65 | String userpass = username + ":" + passwd;
66 | String basicAuth = "Basic " + Base64.getEncoder().encodeToString(userpass.getBytes());
67 | urlConnection.setRequestProperty("Authorization", basicAuth);
68 |
69 | // opens input stream from the HTTP connection
70 | inputStream = urlConnection.getInputStream();
71 |
72 | // opens an output stream to save into file
73 | outputStream = new FileOutputStream(new File(saveFilePath));
74 |
75 | // Save data with blocksize 4096
76 | int bytesRead = -1;
77 | byte[] buffer = new byte[4096];
78 | while ((bytesRead = inputStream.read(buffer)) != -1) {
79 | outputStream.write(buffer, 0, bytesRead);
80 | }
81 |
82 | //Close all resources
83 | urlConnection.disconnect();
84 | inputStream.close();
85 | outputStream.close();
86 |
87 | // Start decompress Thread
88 | new Thread(new ZipHandler(new File(saveFilePath), outputDir)).start();
89 | } catch (Exception e) {
90 | e.printStackTrace();
91 | } finally {
92 | }
93 |
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/TanDEM-X 90/src/main/java/main/Main.java:
--------------------------------------------------------------------------------
1 | package main;
2 |
3 | import java.io.File;
4 | import java.net.URI;
5 | import java.nio.charset.Charset;
6 | import java.nio.file.Files;
7 | import java.nio.file.Paths;
8 | import java.util.List;
9 | import java.util.concurrent.ExecutorService;
10 | import java.util.concurrent.Executors;
11 | import java.util.concurrent.TimeUnit;
12 |
13 | public class Main {
14 | private final File inputList;
15 | private final File outputDir;
16 | private final String username;
17 | private final String passwd;
18 | private final int threads;
19 |
20 | /**
21 | * @param inputList
22 | * @param outputDir
23 | * @param username
24 | * @param passwd
25 | */
26 | public Main(File inputList, File outputDir, String username, String passwd, int threads) {
27 | this.inputList = inputList;
28 | this.outputDir = outputDir;
29 | this.username = username;
30 | this.passwd = passwd;
31 | this.threads = threads;
32 |
33 | parseInputFile();
34 | }
35 |
36 | /**
37 | * Reads all lines from the urllist and add them to an executer service to
38 | * progress in parallel threads
39 | */
40 | private void parseInputFile() {
41 | try {
42 | ExecutorService pool = Executors.newFixedThreadPool(threads);
43 | URI uri = inputList.toURI();
44 |
45 | // Read all lines to list
46 | List lines = Files.readAllLines(Paths.get(uri), Charset.defaultCharset());
47 |
48 | // Set Number of rows to progressDialog to clac time to go
49 | ProgressDialog dialog = ProgressDialog.getInstance();
50 | dialog.setMax(lines.size());
51 |
52 | // adding jobs
53 | for (String line : lines) {
54 | pool.submit(new DownloadTask(line, outputDir, username, passwd));
55 | }
56 |
57 | pool.shutdown();
58 | pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
59 | // all tasks have now finished (unless an exception is thrown above)
60 | } catch (Exception e) {
61 | e.printStackTrace();
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/TanDEM-X 90/src/main/java/main/ProgressDialog.java:
--------------------------------------------------------------------------------
1 | package main;
2 |
3 | import me.tongfei.progressbar.ProgressBar;
4 |
5 | /**
6 | * Progressbar Singleton
7 | *
8 | * @author crypto
9 | *
10 | */
11 | public class ProgressDialog {
12 | private static ProgressDialog instance;
13 | private ProgressBar progressBar = null;
14 |
15 | public synchronized static ProgressDialog getInstance() {
16 | if (ProgressDialog.instance == null) {
17 | ProgressDialog.instance = new ProgressDialog();
18 | }
19 | return ProgressDialog.instance;
20 | }
21 |
22 | // Create Progressbar
23 | private ProgressDialog() {
24 | progressBar = new ProgressBar("Downloading...", 0);
25 | }
26 |
27 | /**
28 | * Add +1 to progressbar
29 | */
30 | public synchronized void addOne() {
31 | progressBar.step();
32 | }
33 |
34 | /**
35 | * Set Max progress to progressBar
36 | *
37 | * @param i
38 | */
39 | public synchronized void setMax(int i) {
40 | progressBar.maxHint(i);
41 | }
42 | }
--------------------------------------------------------------------------------
/TanDEM-X 90/src/main/java/main/Start.java:
--------------------------------------------------------------------------------
1 | package main;
2 |
3 | import java.io.File;
4 |
5 | import org.apache.commons.cli.CommandLine;
6 | import org.apache.commons.cli.CommandLineParser;
7 | import org.apache.commons.cli.DefaultParser;
8 | import org.apache.commons.cli.HelpFormatter;
9 | import org.apache.commons.cli.Options;
10 | import org.apache.commons.cli.ParseException;
11 |
12 | /**
13 | * Start class managing the cmd options
14 | *
15 | * @author crypto
16 | *
17 | */
18 | public class Start {
19 |
20 | public static void main(String[] args) throws ParseException {
21 | //Adding options
22 | Options options = new Options();
23 | options.addOption("i", "input-urllist", true, "Specify the input url list")
24 | .addOption("o", "output", true, "Specify a output for extracted tiff file")
25 | .addOption("u", "username", true, "Your https://geoservice.dlr.de login username (=email)")
26 | .addOption("p", "password", true, "Your https://geoservice.dlr.de password")
27 | .addOption("n","number-of-threads", true, "Insert a number of download threads default is 4");
28 |
29 | HelpFormatter formatter = new HelpFormatter();
30 | formatter.printHelp("TanDEM-X-90-Downloader", null, options,
31 | "Sample ustage:\njava -jar <> -i=C:\\ -o=C:\\out -u=test@test.test -p=pass");
32 |
33 | // Parsed vars
34 | File inputList = null;
35 | File outputDir = null;
36 | String username = null;
37 | String passwd = null;
38 | int threads = 4;
39 |
40 | // Create a parser
41 | CommandLineParser parser = new DefaultParser();
42 | CommandLine cmd = parser.parse(options, args);
43 |
44 | // Get input
45 | if (cmd.hasOption("i")) {
46 | inputList = new File(cmd.getOptionValue("i"));
47 | } else {
48 | error("Plase specify a input file for the url list");
49 | }
50 | // Get output
51 | if (cmd.hasOption("o")) {
52 | outputDir = new File(cmd.getOptionValue("o"));
53 | //Create folderstructure if not exists
54 | if (!outputDir.exists()) {
55 | outputDir.mkdirs();
56 | }
57 | } else {
58 | error("Plase specify a output dir");
59 | }
60 | // Get usernmae
61 | if (cmd.hasOption("u")) {
62 | username = cmd.getOptionValue("u");
63 | } else {
64 | error("Plase specify a username");
65 | }
66 | // Get passwd
67 | if (cmd.hasOption("p")) {
68 | passwd = cmd.getOptionValue("p");
69 | } else {
70 | error("Plase specify a passwd");
71 | }
72 | // Get Threads
73 | if (cmd.hasOption("n")) {
74 | threads = Integer.parseInt(cmd.getOptionValue("n"));
75 | }
76 |
77 | //Give args to Mainclass
78 | new Main(inputList, outputDir, username, passwd, threads);
79 | }
80 |
81 | /**
82 | * Errormessage will be shown as error and system is shutdown
83 | *
84 | * @param msg
85 | */
86 | private static void error(String msg) {
87 | System.err.println("\n\nERROR: " + msg);
88 | System.exit(0);
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/TanDEM-X 90/src/main/java/main/ZipHandler.java:
--------------------------------------------------------------------------------
1 | package main;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.util.Enumeration;
6 |
7 | import org.apache.commons.io.FileUtils;
8 |
9 | import net.lingala.zip4j.core.ZipFile;
10 | import net.lingala.zip4j.exception.ZipException;
11 |
12 | public class ZipHandler implements Runnable {
13 |
14 | private File input;
15 | private File output;
16 |
17 | /**
18 | * @param input
19 | * @param output
20 | */
21 | public ZipHandler(File input, File output) {
22 | this.input = input;
23 | this.output = output;
24 | }
25 |
26 | /**
27 | * Copy DEM file from zip to the output dir and delete extracted dir and zip
28 | * file
29 | * @throws IOException
30 | */
31 | private void copyDEMtoRoot() throws IOException {
32 | // Get extracted File name
33 | java.util.zip.ZipFile zip = new java.util.zip.ZipFile(input);
34 | Enumeration> entries = zip.entries();
35 | File extreactedFile = new File(output.getPath() + File.separator + entries.nextElement());
36 | zip.close();
37 |
38 | // DEM File location
39 | File tifLocation = new File(extreactedFile.getPath() + File.separator + "DEM");
40 | for (String tifFilePath : tifLocation.list()) {
41 | // Rename (mv tif to output dir)
42 | File tif = new File(tifLocation + File.separator + tifFilePath);
43 | tif.renameTo(new File(output.getPath() + File.separator + tif.getName()));
44 | }
45 |
46 | // Delete extracted dir
47 | FileUtils.deleteDirectory(extreactedFile);
48 | // Delete zip
49 | FileUtils.forceDelete(input);
50 | }
51 |
52 | @Override
53 | public void run() {
54 | try {
55 | unzip();
56 | copyDEMtoRoot();
57 |
58 | // Adding progressbar ++ to show progress
59 | ProgressDialog.getInstance().addOne();
60 | } catch (ZipException | IOException e) {
61 | e.printStackTrace();
62 | }
63 | }
64 |
65 | /**
66 | * Unzip file
67 | *
68 | * @throws ZipException
69 | */
70 | private void unzip() throws ZipException {
71 | ZipFile zipFile = new ZipFile(input);
72 | zipFile.extractAll(output.getPath());
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/TanDEM90mDownloader.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Developer66/open-elevation/1d21902eb5cb9b80c0760466592ee004702224b2/TanDEM90mDownloader.jar
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-architect
--------------------------------------------------------------------------------
/create-dataset.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | OUTDIR="data"
4 | #mkdir $OUTDIR
5 |
6 | cd $OUTDIR
7 |
8 | echo Downloading data...
9 | ../download-srtm-data.sh
10 |
11 | echo create tiles: SRTM_NE_250m
12 | ../create-tiles.sh SRTM_NE_250m.tif 10 10 &&
13 | rm -rf SRTM_NE_250m.tif
14 |
15 | echo create tiles: SRTM_SE_250m
16 | ../create-tiles.sh SRTM_SE_250m.tif 10 10 &&
17 | rm -rf SRTM_SE_250m.tif
18 |
19 | echo create tiles: SRTM_W_250m
20 | ../create-tiles.sh SRTM_W_250m.tif 10 20 &&
21 | rm -rf SRTM_W_250m.tif
22 |
23 | echo removing unused data
24 | rm -rf readme.txt TIFF-Files-here.md
25 |
--------------------------------------------------------------------------------
/create-tiles.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Original Source: https://github.com/mapbox/gdal-polygonize-test
3 | set -eu
4 |
5 | raster=$1
6 | xtiles=$2
7 | ytiles=$3
8 |
9 | # get raster bounds
10 | ul=($(gdalinfo $raster | grep '^Upper Left' | sed -e 's/[a-zA-Z ]*(//' -e 's/).*//' -e 's/,/ /'))
11 | lr=($(gdalinfo $raster | grep '^Lower Right' | sed -e 's/[a-zA-Z ]*(//' -e 's/).*//' -e 's/,/ /'))
12 |
13 | xmin=${ul[0]}
14 | xsize=$(echo "${lr[0]} - $xmin" | bc)
15 | ysize=$(echo "${ul[1]} - ${lr[1]}" | bc)
16 |
17 | xdif=$(echo "$xsize/$xtiles" | bc -l)
18 |
19 | for x in $(eval echo {0..$(($xtiles-1))}); do
20 | xmax=$(echo "$xmin + $xdif" | bc)
21 | ymax=${ul[1]}
22 | ydif=$(echo "$ysize/$ytiles" | bc -l)
23 |
24 | for y in $(eval echo {0..$((ytiles-1))}); do
25 | ymin=$(echo "$ymax - $ydif" | bc)
26 |
27 | # Create chunk of source raster
28 | gdal_translate -q \
29 | -projwin $xmin $ymax $xmax $ymin \
30 | -of GTiff \
31 | $raster ${raster%.tif}_${x}_${y}.tif
32 |
33 | ymax=$ymin
34 | done
35 | xmin=$xmax
36 | done
37 |
--------------------------------------------------------------------------------
/data/TIFF-Files-here.md:
--------------------------------------------------------------------------------
1 | Your geoTiff's have to be in this folder.
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.1'
2 | services:
3 | openelevation:
4 | container_name: open-elevation
5 | build:
6 | context: .
7 | dockerfile: ./docker/Dockerfile
8 | networks:
9 | - elevnet
10 | ports:
11 | - "8080:10000"
12 | volumes:
13 | - ./data:/code/data
14 | networks:
15 | elevnet:
16 | driver: bridge
17 |
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM geodata/gdal:2.1.3
2 |
3 | ADD ./requirements.txt .
4 |
5 | RUN pip install -r requirements.txt
6 |
7 | RUN apt-get update
8 |
9 | RUN apt-get install -y libspatialindex-dev unar bc
10 |
11 | RUN mkdir /code
12 |
13 | ADD . /code/
14 |
15 | WORKDIR /code
16 |
17 | CMD python server.py
18 |
19 | EXPOSE 8080
20 |
--------------------------------------------------------------------------------
/docs/api.md:
--------------------------------------------------------------------------------
1 | # Public API Documentation
2 |
3 | Open-Elevation's API is extremely simple -- after all, it fits a single, specific, simple task. There is **only one endpoint**, which is documented here.
4 |
5 | ## `GET /api/v1/lookup`
6 |
7 | Returns ("looks up") the elevation at one or more `(latitude,longitude)` points.
8 |
9 | The GET API is limited to **1024** bytes in the request line. If you plan on making large requests, consider using the POST api.
10 |
11 | ### Parameters:
12 |
13 | * **`locations`**: List of locations, separated by `|` in `latitude, longitude` format, similar to the Google Elevation API.
14 |
15 | ### Response format
16 |
17 | A JSON object with a single list of results, in the `results` field is returned. Each result contains `latitude`, `longitude` and `elevation`. The results are in the same order as the request parameters. **Elevation is in meters**.
18 |
19 | If there is no recorded elevation at the provided coordinate, sea level (0 meters) is returned.
20 |
21 | ```json
22 | {
23 | "results":
24 | [
25 | {
26 | "latitude": ...,
27 | "longitude": ...,
28 | "elevation": ...
29 | },
30 | ...
31 | ]
32 | }
33 | ```
34 |
35 |
36 | ### Example:
37 |
38 | #### Request
39 |
40 | ```
41 | curl https://api.open-elevation.com/api/v1/lookup\?locations\=10,10\|20,20\|41.161758,-8.583933
42 | ```
43 |
44 | #### Response
45 |
46 | ```json
47 | {
48 | "results":
49 | [
50 | {
51 | "longitude":10.0,
52 | "elevation":515,
53 | "latitude":10.0
54 | },
55 | {
56 | "longitude":20.0,
57 | "elevation":545,
58 | "latitude":20.0
59 | },
60 | {
61 | "latitude":41.161758,
62 | "elevation":117,
63 | "longitude":-8.583933
64 | }
65 | ]
66 | }
67 | ```
68 |
69 |
70 | ## `POST /api/v1/lookup`
71 |
72 | Returns ("looks up") the elevation at one or more `(latitude,longitude)` points.
73 |
74 | The POST API currently has no limit
75 |
76 | ### Parameters:
77 |
78 | * A JSON (and respective headers) is required with the format:
79 | ```
80 | {
81 | "locations":
82 | [
83 | {
84 | "latitude": ...,
85 | "longitude": ...
86 | },
87 | ...
88 | }
89 | ```
90 |
91 |
92 | ### Response format
93 |
94 | A JSON object with a single list of results, in the `results` field is returned. Each result contains `latitude`, `longitude` and `elevation`. The results are in the same order as the request parameters. **Elevation is in meters**.
95 |
96 | If there is no recorded elevation at the provided coordinate, sea level (0 meters) is returned.
97 |
98 | ```json
99 | {
100 | "results":
101 | [
102 | {
103 | "latitude": ...,
104 | "longitude": ...,
105 | "elevation": ...
106 | },
107 | ...
108 | ]
109 | }
110 | ```
111 |
112 |
113 | ### Example:
114 |
115 | #### Request
116 |
117 | ```
118 | curl -X POST \
119 | https://api.open-elevation.com/api/v1/lookup \
120 | -H 'Accept: application/json' \
121 | -H 'Content-Type: application/json' \
122 | -d '{
123 | "locations":
124 | [
125 | {
126 | "latitude": 10,
127 | "longitude": 10
128 | },
129 | {
130 | "latitude":20,
131 | "longitude": 20
132 | },
133 | {
134 | "latitude":41.161758,
135 | "longitude":-8.583933
136 | }
137 | ]
138 |
139 | }'
140 | ```
141 |
142 | #### Response
143 |
144 | ```json
145 | {
146 | "results":
147 | [
148 | {
149 | "longitude":10.0,
150 | "elevation":515,
151 | "latitude":10.0
152 | },
153 | {
154 | "longitude":20.0,
155 | "elevation":545,
156 | "latitude":20.0
157 | },
158 | {
159 | "latitude":41.161758,
160 | "elevation":117,
161 | "longitude":-8.583933
162 | }
163 | ]
164 | }
165 | ```
166 |
--------------------------------------------------------------------------------
/download-srtm-data.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | wget -nc http://srtm.csi.cgiar.org/wp-content/uploads/files/250m/SRTM_NE_250m_TIF.rar &&
4 | wget -nc http://srtm.csi.cgiar.org/wp-content/uploads/files/250m/SRTM_W_250m_TIF.rar &&
5 | wget -nc http://srtm.csi.cgiar.org/wp-content/uploads/files/250m/SRTM_SE_250m_TIF.rar &&
6 | unar -f SRTM_NE_250m_TIF.rar -o ../data -D &&
7 | unar -f SRTM_SE_250m_TIF.rar -o ../data -D &&
8 | unar -f SRTM_W_250m_TIF.rar -o ../data -D &&
9 |
10 | echo
11 | echo
12 | echo *******************
13 | echo Deleting rars
14 | echo *******************
15 | rm -rf SRTM_W_250m_TIF.rar SRTM_SE_250m_TIF.rar SRTM_NE_250m_TIF.rar
16 |
--------------------------------------------------------------------------------
/gdal_interfaces.py:
--------------------------------------------------------------------------------
1 | import gdal, osr
2 | from lazy import lazy
3 | from pprint import pprint
4 | from os import listdir
5 | from os.path import isfile, join
6 | import json
7 | from rtree import index
8 |
9 | # Originally based on https://stackoverflow.com/questions/13439357/extract-point-from-raster-in-gdal
10 | class GDALInterface(object):
11 | SEA_LEVEL = 0
12 | def __init__(self, tif_path):
13 | super(GDALInterface, self).__init__()
14 | self.tif_path = tif_path
15 | self.loadMetadata()
16 |
17 | def get_corner_coords(self):
18 | ulx, xres, xskew, uly, yskew, yres = self.geo_transform
19 | lrx = ulx + (self.src.RasterXSize * xres)
20 | lry = uly + (self.src.RasterYSize * yres)
21 | return {
22 | 'TOP_LEFT': (ulx, uly),
23 | 'TOP_RIGHT': (lrx, uly),
24 | 'BOTTOM_LEFT': (ulx, lry),
25 | 'BOTTOM_RIGHT': (lrx, lry),
26 | }
27 |
28 | def loadMetadata(self):
29 | # open the raster and its spatial reference
30 | self.src = gdal.Open(self.tif_path)
31 |
32 | if self.src is None:
33 | raise Exception('Could not load GDAL file "%s"' % self.tif_path)
34 | spatial_reference_raster = osr.SpatialReference(self.src.GetProjection())
35 |
36 | # get the WGS84 spatial reference
37 | spatial_reference = osr.SpatialReference()
38 | spatial_reference.ImportFromEPSG(4326) # WGS84
39 |
40 | # coordinate transformation
41 | self.coordinate_transform = osr.CoordinateTransformation(spatial_reference, spatial_reference_raster)
42 | gt = self.geo_transform = self.src.GetGeoTransform()
43 | dev = (gt[1] * gt[5] - gt[2] * gt[4])
44 | self.geo_transform_inv = (gt[0], gt[5] / dev, -gt[2] / dev,
45 | gt[3], -gt[4] / dev, gt[1] / dev)
46 |
47 |
48 |
49 | @lazy
50 | def points_array(self):
51 | b = self.src.GetRasterBand(1)
52 | return b.ReadAsArray()
53 |
54 | def print_statistics(self):
55 | print(self.src.GetRasterBand(1).GetStatistics(True, True))
56 |
57 |
58 | def lookup(self, lat, lon):
59 | try:
60 |
61 | # get coordinate of the raster
62 | xgeo, ygeo, zgeo = self.coordinate_transform.TransformPoint(lon, lat, 0)
63 |
64 | # convert it to pixel/line on band
65 | u = xgeo - self.geo_transform_inv[0]
66 | v = ygeo - self.geo_transform_inv[3]
67 | # FIXME this int() is probably bad idea, there should be half cell size thing needed
68 | xpix = int(self.geo_transform_inv[1] * u + self.geo_transform_inv[2] * v)
69 | ylin = int(self.geo_transform_inv[4] * u + self.geo_transform_inv[5] * v)
70 |
71 | # look the value up
72 | v = self.points_array[ylin, xpix]
73 |
74 | return v if v != -32768 else self.SEA_LEVEL
75 | except Exception as e:
76 | print(e)
77 | return self.SEA_LEVEL
78 |
79 | def close(self):
80 | self.src = None
81 |
82 | def __enter__(self):
83 | return self
84 |
85 | def __exit__(self, type, value, traceback):
86 | self.close()
87 |
88 | class GDALTileInterface(object):
89 | def __init__(self, tiles_folder, summary_file, open_interfaces_size=5):
90 | super(GDALTileInterface, self).__init__()
91 | self.tiles_folder = tiles_folder
92 | self.summary_file = summary_file
93 | self.index = index.Index()
94 | self.cached_open_interfaces = []
95 | self.cached_open_interfaces_dict = {}
96 | self.open_interfaces_size = open_interfaces_size
97 |
98 | def _open_gdal_interface(self, path):
99 | if path in self.cached_open_interfaces_dict:
100 | interface = self.cached_open_interfaces_dict[path]
101 | self.cached_open_interfaces.remove(path)
102 | self.cached_open_interfaces += [path]
103 |
104 | return interface
105 | else:
106 |
107 | interface = GDALInterface(path)
108 | self.cached_open_interfaces += [path]
109 | self.cached_open_interfaces_dict[path] = interface
110 |
111 | if len(self.cached_open_interfaces) > self.open_interfaces_size:
112 | last_interface_path = self.cached_open_interfaces.pop(0)
113 | last_interface = self.cached_open_interfaces_dict[last_interface_path]
114 | last_interface.close()
115 |
116 | self.cached_open_interfaces_dict[last_interface_path] = None
117 | del self.cached_open_interfaces_dict[last_interface_path]
118 |
119 | return interface
120 |
121 | def _all_files(self):
122 | return [f for f in listdir(self.tiles_folder) if isfile(join(self.tiles_folder, f)) and f.endswith(u'.tif')]
123 |
124 | def create_summary_json(self):
125 | all_coords = []
126 | for file in self._all_files():
127 |
128 | full_path = join(self.tiles_folder,file)
129 | i = self._open_gdal_interface(full_path)
130 | coords = i.get_corner_coords()
131 | all_coords += [
132 | {
133 | 'file': full_path,
134 | 'coords': ( coords['BOTTOM_RIGHT'][1], # latitude min
135 | coords['TOP_RIGHT'][1], # latitude max
136 | coords['TOP_LEFT'][0], # longitude min
137 | coords['TOP_RIGHT'][0], # longitude max
138 |
139 | )
140 | }
141 | ]
142 |
143 | with open(self.summary_file, 'w') as f:
144 | json.dump(all_coords, f)
145 |
146 | self.all_coords = all_coords
147 |
148 | self._build_index()
149 |
150 | def read_summary_json(self):
151 | with open(self.summary_file) as f:
152 | self.all_coords = json.load(f)
153 |
154 | self._build_index()
155 |
156 | def lookup(self, lat, lng):
157 |
158 | nearest = list(self.index.nearest((lat, lng), 1, objects=True))
159 |
160 | if not nearest:
161 | raise Exception('Invalid latitude/longitude')
162 | else:
163 | coords = nearest[0].object
164 |
165 | gdal_interface = self._open_gdal_interface(coords['file'])
166 | return int(gdal_interface.lookup(lat, lng))
167 |
168 | def _build_index(self):
169 | index_id = 1
170 | for e in self.all_coords:
171 | e['index_id'] = index_id
172 | left, bottom, right, top = (e['coords'][0], e['coords'][2], e['coords'][1], e['coords'][3])
173 | self.index.insert( index_id, (left, bottom, right, top), obj=e)
174 |
--------------------------------------------------------------------------------
/gdal_interfaces.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Developer66/open-elevation/1d21902eb5cb9b80c0760466592ee004702224b2/gdal_interfaces.pyc
--------------------------------------------------------------------------------
/license.md:
--------------------------------------------------------------------------------
1 | The GNU General Public License, Version 2, June 1991 (GPLv2)
2 | ============================================================
3 |
4 | > Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5 | > 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
6 |
7 | Everyone is permitted to copy and distribute verbatim copies of this license
8 | document, but changing it is not allowed.
9 |
10 |
11 | Preamble
12 | --------
13 |
14 | The licenses for most software are designed to take away your freedom to share
15 | and change it. By contrast, the GNU General Public License is intended to
16 | guarantee your freedom to share and change free software--to make sure the
17 | software is free for all its users. This General Public License applies to most
18 | of the Free Software Foundation's software and to any other program whose
19 | authors commit to using it. (Some other Free Software Foundation software is
20 | covered by the GNU Lesser General Public License instead.) You can apply it to
21 | your programs, too.
22 |
23 | When we speak of free software, we are referring to freedom, not price. Our
24 | General Public Licenses are designed to make sure that you have the freedom to
25 | distribute copies of free software (and charge for this service if you wish),
26 | that you receive source code or can get it if you want it, that you can change
27 | the software or use pieces of it in new free programs; and that you know you can
28 | do these things.
29 |
30 | To protect your rights, we need to make restrictions that forbid anyone to deny
31 | you these rights or to ask you to surrender the rights. These restrictions
32 | translate to certain responsibilities for you if you distribute copies of the
33 | software, or if you modify it.
34 |
35 | For example, if you distribute copies of such a program, whether gratis or for a
36 | fee, you must give the recipients all the rights that you have. You must make
37 | sure that they, too, receive or can get the source code. And you must show them
38 | these terms so they know their rights.
39 |
40 | We protect your rights with two steps: (1) copyright the software, and (2) offer
41 | you this license which gives you legal permission to copy, distribute and/or
42 | modify the software.
43 |
44 | Also, for each author's protection and ours, we want to make certain that
45 | everyone understands that there is no warranty for this free software. If the
46 | software is modified by someone else and passed on, we want its recipients to
47 | know that what they have is not the original, so that any problems introduced by
48 | others will not reflect on the original authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software patents. We wish
51 | to avoid the danger that redistributors of a free program will individually
52 | obtain patent licenses, in effect making the program proprietary. To prevent
53 | this, we have made it clear that any patent must be licensed for everyone's free
54 | use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and modification
57 | follow.
58 |
59 |
60 | Terms And Conditions For Copying, Distribution And Modification
61 | ---------------------------------------------------------------
62 |
63 | **0.** This License applies to any program or other work which contains a notice
64 | placed by the copyright holder saying it may be distributed under the terms of
65 | this General Public License. The "Program", below, refers to any such program or
66 | work, and a "work based on the Program" means either the Program or any
67 | derivative work under copyright law: that is to say, a work containing the
68 | Program or a portion of it, either verbatim or with modifications and/or
69 | translated into another language. (Hereinafter, translation is included without
70 | limitation in the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not covered by
73 | this License; they are outside its scope. The act of running the Program is not
74 | restricted, and the output from the Program is covered only if its contents
75 | constitute a work based on the Program (independent of having been made by
76 | running the Program). Whether that is true depends on what the Program does.
77 |
78 | **1.** You may copy and distribute verbatim copies of the Program's source code
79 | as you receive it, in any medium, provided that you conspicuously and
80 | appropriately publish on each copy an appropriate copyright notice and
81 | disclaimer of warranty; keep intact all the notices that refer to this License
82 | and to the absence of any warranty; and give any other recipients of the Program
83 | a copy of this License along with the Program.
84 |
85 | You may charge a fee for the physical act of transferring a copy, and you may at
86 | your option offer warranty protection in exchange for a fee.
87 |
88 | **2.** You may modify your copy or copies of the Program or any portion of it,
89 | thus forming a work based on the Program, and copy and distribute such
90 | modifications or work under the terms of Section 1 above, provided that you also
91 | meet all of these conditions:
92 |
93 | * **a)** You must cause the modified files to carry prominent notices stating
94 | that you changed the files and the date of any change.
95 |
96 | * **b)** You must cause any work that you distribute or publish, that in whole
97 | or in part contains or is derived from the Program or any part thereof, to
98 | be licensed as a whole at no charge to all third parties under the terms of
99 | this License.
100 |
101 | * **c)** If the modified program normally reads commands interactively when
102 | run, you must cause it, when started running for such interactive use in the
103 | most ordinary way, to print or display an announcement including an
104 | appropriate copyright notice and a notice that there is no warranty (or
105 | else, saying that you provide a warranty) and that users may redistribute
106 | the program under these conditions, and telling the user how to view a copy
107 | of this License. (Exception: if the Program itself is interactive but does
108 | not normally print such an announcement, your work based on the Program is
109 | not required to print an announcement.)
110 |
111 | These requirements apply to the modified work as a whole. If identifiable
112 | sections of that work are not derived from the Program, and can be reasonably
113 | considered independent and separate works in themselves, then this License, and
114 | its terms, do not apply to those sections when you distribute them as separate
115 | works. But when you distribute the same sections as part of a whole which is a
116 | work based on the Program, the distribution of the whole must be on the terms of
117 | this License, whose permissions for other licensees extend to the entire whole,
118 | and thus to each and every part regardless of who wrote it.
119 |
120 | Thus, it is not the intent of this section to claim rights or contest your
121 | rights to work written entirely by you; rather, the intent is to exercise the
122 | right to control the distribution of derivative or collective works based on the
123 | Program.
124 |
125 | In addition, mere aggregation of another work not based on the Program with the
126 | Program (or with a work based on the Program) on a volume of a storage or
127 | distribution medium does not bring the other work under the scope of this
128 | License.
129 |
130 | **3.** You may copy and distribute the Program (or a work based on it, under
131 | Section 2) in object code or executable form under the terms of Sections 1 and 2
132 | above provided that you also do one of the following:
133 |
134 | * **a)** Accompany it with the complete corresponding machine-readable source
135 | code, which must be distributed under the terms of Sections 1 and 2 above on
136 | a medium customarily used for software interchange; or,
137 |
138 | * **b)** Accompany it with a written offer, valid for at least three years, to
139 | give any third party, for a charge no more than your cost of physically
140 | performing source distribution, a complete machine-readable copy of the
141 | corresponding source code, to be distributed under the terms of Sections 1
142 | and 2 above on a medium customarily used for software interchange; or,
143 |
144 | * **c)** Accompany it with the information you received as to the offer to
145 | distribute corresponding source code. (This alternative is allowed only for
146 | noncommercial distribution and only if you received the program in object
147 | code or executable form with such an offer, in accord with Subsection b
148 | above.)
149 |
150 | The source code for a work means the preferred form of the work for making
151 | modifications to it. For an executable work, complete source code means all the
152 | source code for all modules it contains, plus any associated interface
153 | definition files, plus the scripts used to control compilation and installation
154 | of the executable. However, as a special exception, the source code distributed
155 | need not include anything that is normally distributed (in either source or
156 | binary form) with the major components (compiler, kernel, and so on) of the
157 | operating system on which the executable runs, unless that component itself
158 | accompanies the executable.
159 |
160 | If distribution of executable or object code is made by offering access to copy
161 | from a designated place, then offering equivalent access to copy the source code
162 | from the same place counts as distribution of the source code, even though third
163 | parties are not compelled to copy the source along with the object code.
164 |
165 | **4.** You may not copy, modify, sublicense, or distribute the Program except as
166 | expressly provided under this License. Any attempt otherwise to copy, modify,
167 | sublicense or distribute the Program is void, and will automatically terminate
168 | your rights under this License. However, parties who have received copies, or
169 | rights, from you under this License will not have their licenses terminated so
170 | long as such parties remain in full compliance.
171 |
172 | **5.** You are not required to accept this License, since you have not signed
173 | it. However, nothing else grants you permission to modify or distribute the
174 | Program or its derivative works. These actions are prohibited by law if you do
175 | not accept this License. Therefore, by modifying or distributing the Program (or
176 | any work based on the Program), you indicate your acceptance of this License to
177 | do so, and all its terms and conditions for copying, distributing or modifying
178 | the Program or works based on it.
179 |
180 | **6.** Each time you redistribute the Program (or any work based on the
181 | Program), the recipient automatically receives a license from the original
182 | licensor to copy, distribute or modify the Program subject to these terms and
183 | conditions. You may not impose any further restrictions on the recipients'
184 | exercise of the rights granted herein. You are not responsible for enforcing
185 | compliance by third parties to this License.
186 |
187 | **7.** If, as a consequence of a court judgment or allegation of patent
188 | infringement or for any other reason (not limited to patent issues), conditions
189 | are imposed on you (whether by court order, agreement or otherwise) that
190 | contradict the conditions of this License, they do not excuse you from the
191 | conditions of this License. If you cannot distribute so as to satisfy
192 | simultaneously your obligations under this License and any other pertinent
193 | obligations, then as a consequence you may not distribute the Program at all.
194 | For example, if a patent license would not permit royalty-free redistribution of
195 | the Program by all those who receive copies directly or indirectly through you,
196 | then the only way you could satisfy both it and this License would be to refrain
197 | entirely from distribution of the Program.
198 |
199 | If any portion of this section is held invalid or unenforceable under any
200 | particular circumstance, the balance of the section is intended to apply and the
201 | section as a whole is intended to apply in other circumstances.
202 |
203 | It is not the purpose of this section to induce you to infringe any patents or
204 | other property right claims or to contest validity of any such claims; this
205 | section has the sole purpose of protecting the integrity of the free software
206 | distribution system, which is implemented by public license practices. Many
207 | people have made generous contributions to the wide range of software
208 | distributed through that system in reliance on consistent application of that
209 | system; it is up to the author/donor to decide if he or she is willing to
210 | distribute software through any other system and a licensee cannot impose that
211 | choice.
212 |
213 | This section is intended to make thoroughly clear what is believed to be a
214 | consequence of the rest of this License.
215 |
216 | **8.** If the distribution and/or use of the Program is restricted in certain
217 | countries either by patents or by copyrighted interfaces, the original copyright
218 | holder who places the Program under this License may add an explicit
219 | geographical distribution limitation excluding those countries, so that
220 | distribution is permitted only in or among countries not thus excluded. In such
221 | case, this License incorporates the limitation as if written in the body of this
222 | License.
223 |
224 | **9.** The Free Software Foundation may publish revised and/or new versions of
225 | the General Public License from time to time. Such new versions will be similar
226 | in spirit to the present version, but may differ in detail to address new
227 | problems or concerns.
228 |
229 | Each version is given a distinguishing version number. If the Program specifies
230 | a version number of this License which applies to it and "any later version",
231 | you have the option of following the terms and conditions either of that version
232 | or of any later version published by the Free Software Foundation. If the
233 | Program does not specify a version number of this License, you may choose any
234 | version ever published by the Free Software Foundation.
235 |
236 | **10.** If you wish to incorporate parts of the Program into other free programs
237 | whose distribution conditions are different, write to the author to ask for
238 | permission. For software which is copyrighted by the Free Software Foundation,
239 | write to the Free Software Foundation; we sometimes make exceptions for this.
240 | Our decision will be guided by the two goals of preserving the free status of
241 | all derivatives of our free software and of promoting the sharing and reuse of
242 | software generally.
243 |
244 |
245 | No Warranty
246 | -----------
247 |
248 | **11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
249 | THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
250 | STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
251 | "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
252 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
253 | PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
254 | PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
255 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
256 |
257 | **12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
258 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
259 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
260 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
261 | INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
262 | BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
263 | FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER
264 | OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
265 |
--------------------------------------------------------------------------------
/open-elevation.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Open-Elevation Server
3 | After=network.target
4 |
5 | [Service]
6 | Type=simple
7 | User=<>
8 | WorkingDirectory=/home/<>/<>
9 | ExecStart=/usr/bin/env python <>/server.py
10 | Restart=always
11 | RestartSec=1
12 |
13 | [Install]
14 | WantedBy=multi-user.target
15 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | lazy==1.3
2 | GDAL==2.1.3
3 | bottle==0.12.13
4 | rtree==0.8.3
5 | gunicorn==19.7.1
--------------------------------------------------------------------------------
/server.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | import bottle
4 | from bottle import route, run, request, response, hook
5 |
6 | from gdal_interfaces import GDALTileInterface
7 |
8 |
9 | class InternalException(ValueError):
10 | """
11 | Utility exception class to handle errors internally and return error codes to the client
12 | """
13 | pass
14 |
15 |
16 | """
17 | Initialize a global interface. This can grow quite large, because it has a cache.
18 | """
19 | interface = GDALTileInterface('data/', 'data/summary.json')
20 | interface.create_summary_json()
21 |
22 | def get_elevation(lat, lng):
23 | """
24 | Get the elevation at point (lat,lng) using the currently opened interface
25 | :param lat:
26 | :param lng:
27 | :return:
28 | """
29 | try:
30 | elevation = interface.lookup(lat, lng)
31 | except:
32 | return {
33 | 'latitude': lat,
34 | 'longitude': lng,
35 | 'error': 'No such coordinate (%s, %s)' % (lat, lng)
36 | }
37 |
38 | return {
39 | 'latitude': lat,
40 | 'longitude': lng,
41 | 'elevation': elevation
42 | }
43 |
44 |
45 | @hook('after_request')
46 | def enable_cors():
47 | """
48 | Enable CORS support.
49 | :return:
50 | """
51 | response.headers['Access-Control-Allow-Origin'] = '*'
52 | response.headers['Access-Control-Allow-Methods'] = 'PUT, GET, POST, DELETE, OPTIONS'
53 | response.headers['Access-Control-Allow-Headers'] = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'
54 |
55 |
56 | def lat_lng_from_location(location_with_comma):
57 | """
58 | Parse the latitude and longitude of a location in the format "xx.xxx,yy.yyy" (which we accept as a query string)
59 | :param location_with_comma:
60 | :return:
61 | """
62 | try:
63 | lat, lng = [float(i) for i in location_with_comma.split(',')]
64 | return lat, lng
65 | except:
66 | raise InternalException(json.dumps({'error': 'Bad parameter format "%s".' % location_with_comma}))
67 |
68 |
69 | def query_to_locations():
70 | """
71 | Grab a list of locations from the query and turn them into [(lat,lng),(lat,lng),...]
72 | :return:
73 | """
74 | locations = request.query.locations
75 | if not locations:
76 | raise InternalException(json.dumps({'error': '"Locations" is required.'}))
77 |
78 | return [lat_lng_from_location(l) for l in locations.split('|')]
79 |
80 |
81 | def body_to_locations():
82 | """
83 | Grab a list of locations from the body and turn them into [(lat,lng),(lat,lng),...]
84 | :return:
85 | """
86 | try:
87 | locations = request.json.get('locations', None)
88 | except Exception:
89 | raise InternalException(json.dumps({'error': 'Invalid JSON.'}))
90 |
91 | if not locations:
92 | raise InternalException(json.dumps({'error': '"Locations" is required in the body.'}))
93 |
94 | latlng = []
95 | for l in locations:
96 | try:
97 | latlng += [ (l['latitude'],l['longitude']) ]
98 | except KeyError:
99 | raise InternalException(json.dumps({'error': '"%s" is not in a valid format.' % l}))
100 |
101 | return latlng
102 |
103 |
104 | def do_lookup(get_locations_func):
105 | """
106 | Generic method which gets the locations in [(lat,lng),(lat,lng),...] format by calling get_locations_func
107 | and returns an answer ready to go to the client.
108 | :return:
109 | """
110 | try:
111 | locations = get_locations_func()
112 | return {'results': [get_elevation(lat, lng) for (lat, lng) in locations]}
113 | except InternalException as e:
114 | response.status = 400
115 | response.content_type = 'application/json'
116 | return e.args[0]
117 |
118 | # Base Endpoint
119 | URL_ENDPOINT = '/api/v1/lookup'
120 |
121 | # For CORS
122 | @route(URL_ENDPOINT, method=['OPTIONS'])
123 | def cors_handler():
124 | return {}
125 |
126 | @route(URL_ENDPOINT, method=['GET'])
127 | def get_lookup():
128 | """
129 | GET method. Uses query_to_locations.
130 | :return:
131 | """
132 | return do_lookup(query_to_locations)
133 |
134 |
135 | @route(URL_ENDPOINT, method=['POST'])
136 | def post_lookup():
137 | """
138 | GET method. Uses body_to_locations.
139 | :return:
140 | """
141 | return do_lookup(body_to_locations)
142 |
143 | run(host='0.0.0.0', port=10000, server='gunicorn', workers=4)
--------------------------------------------------------------------------------
/swagger/swagger.json:
--------------------------------------------------------------------------------
1 | {
2 | "swagger": "2.0",
3 | "info": {
4 | "version": "2",
5 | "title": "Open Elevation Public API"
6 | },
7 | "schemes": [
8 | "http"
9 | ],
10 | "host": "localhost",
11 | "basePath": "/api/v1",
12 | "paths": {
13 | "/lookup": {
14 | "post": {
15 | "consumes": [
16 | "application/json"
17 | ],
18 | "produces": [
19 | "application/json"
20 | ],
21 | "parameters": [
22 | {
23 | "in": "body",
24 | "name": "locations",
25 | "description": "locations: [{latitude: 42.216667,longitude: 27.416667}]",
26 | "type": "string"
27 | }
28 | ],
29 | "responses": {
30 | "200": {
31 | "description": "Successful response",
32 | "schema": {
33 | "title": "results",
34 | "type": "object",
35 | "properties": {
36 | "success": {
37 | "type": "string"
38 | },
39 | "results": {
40 | "type": "array",
41 | "items": {
42 | "type": "object",
43 | "properties": {
44 | "latitude": {
45 | "type": "number"
46 | },
47 | "longitude": {
48 | "type": "number"
49 | },
50 | "elevation": {
51 | "type": "integer"
52 | }
53 | }
54 | }
55 | }
56 | }
57 | }
58 | },
59 | "400": {
60 | "description": "Bad request"
61 | }
62 | }
63 | },
64 | "get": {
65 | "description": "List altitude for locations.\n",
66 | "parameters": [
67 | {
68 | "in": "query",
69 | "name": "locations",
70 | "description": "locations=42.216667,27.416667",
71 | "type": "string"
72 | }
73 | ],
74 | "responses": {
75 | "200": {
76 | "description": "Successful response",
77 | "schema": {
78 | "title": "results",
79 | "type": "object",
80 | "properties": {
81 | "success": {
82 | "type": "string"
83 | },
84 | "results": {
85 | "type": "array",
86 | "items": {
87 | "type": "object",
88 | "properties": {
89 | "latitude": {
90 | "type": "number"
91 | },
92 | "longitude": {
93 | "type": "number"
94 | },
95 | "elevation": {
96 | "type": "integer"
97 | }
98 | }
99 | }
100 | }
101 | }
102 | }
103 | },
104 | "400": {
105 | "description": "Bad request"
106 | }
107 | }
108 | }
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------