open_topo 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/LICENSE.txt +21 -0
- data/Rakefile +10 -0
- data/lib/open_topo/catalog.rb +17 -0
- data/lib/open_topo/client.rb +68 -0
- data/lib/open_topo/dem_file.rb +23 -0
- data/lib/open_topo/errors/base_error.rb +11 -0
- data/lib/open_topo/errors/convert_error.rb +8 -0
- data/lib/open_topo/errors/file_error.rb +22 -0
- data/lib/open_topo/errors/params_error.rb +8 -0
- data/lib/open_topo/errors/response_error.rb +11 -0
- data/lib/open_topo/net/connection.rb +58 -0
- data/lib/open_topo/net/params/base_params.rb +61 -0
- data/lib/open_topo/net/params/catalog_params.rb +50 -0
- data/lib/open_topo/net/params/contracts/catalog_contract.rb +84 -0
- data/lib/open_topo/net/params/contracts/elevation_contract.rb +23 -0
- data/lib/open_topo/net/params/contracts/globaldem_contract.rb +38 -0
- data/lib/open_topo/net/params/contracts/usgsdem_contract.rb +38 -0
- data/lib/open_topo/net/params/elevation_params.rb +59 -0
- data/lib/open_topo/net/params/globaldem_params.rb +56 -0
- data/lib/open_topo/net/params/usgsdem_params.rb +40 -0
- data/lib/open_topo/net/request.rb +32 -0
- data/lib/open_topo/net/response.rb +64 -0
- data/lib/open_topo/point.rb +24 -0
- data/lib/open_topo/services/dem_converter.rb +56 -0
- data/lib/open_topo/services/response_to_catalog_list_converter.rb +25 -0
- data/lib/open_topo/services/response_to_dem_converter.rb +34 -0
- data/lib/open_topo/services/response_to_point_converter.rb +16 -0
- data/lib/open_topo/version.rb +5 -0
- data/lib/open_topo.rb +8 -0
- data/sig/open_topo.rbs +4 -0
- metadata +286 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 818d3b1e23f9cce9865b4efc13c0f536341c27788eff7b95d504004d09158b9c
|
|
4
|
+
data.tar.gz: 82d26068b2ced4d8ff585afe47600291a2d16a75376d02a867119930e55eb589
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a488cecaf0bf2268228e83a52c600e2eb9db76678d58d008ff4d658160acf3582a8b7ec1ba688124058f38af542433a7c1fe2c24b7e825e47e01ebe3034f9b79
|
|
7
|
+
data.tar.gz: ea3719db6d89955d4fe41063d0594e0163112e16479880b6b2eb24c1b8728c41083f71d86bd239379121164a5ac9d0331b057f5e1ec51cace4fd371a12e9061f
|
data/CHANGELOG.md
ADDED
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
"open_topo" follows [The Ruby Community Conduct Guideline](https://www.ruby-lang.org/en/conduct) in all "collaborative space", which is defined as community communications channels (such as mailing lists, submitted patches, commit comments, etc.):
|
|
4
|
+
|
|
5
|
+
* Participants will be tolerant of opposing views.
|
|
6
|
+
* Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
|
|
7
|
+
* When interpreting the words and actions of others, participants should always assume good intentions.
|
|
8
|
+
* Behaviour which can be reasonably considered harassment will not be tolerated.
|
|
9
|
+
|
|
10
|
+
If you have any concerns about behaviour within this project, please contact us at ["horolskyandrey@gmail.com"](mailto:"horolskyandrey@gmail.com").
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 andrew-kh8
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module OpenTopo
|
|
5
|
+
class Catalog
|
|
6
|
+
attr_reader :full_name, :name, :url, :date_created, :temporal_coverage, :unit
|
|
7
|
+
|
|
8
|
+
def initialize(full_name:, name:, url:, date_created:, temporal_coverage:, unit: "meter")
|
|
9
|
+
@full_name = full_name
|
|
10
|
+
@name = name
|
|
11
|
+
@url = url
|
|
12
|
+
@date_created = date_created
|
|
13
|
+
@temporal_coverage = temporal_coverage
|
|
14
|
+
@unit = unit
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenTopo
|
|
4
|
+
class Client
|
|
5
|
+
API_KEY_NAME = "OPEN_TOPOGRAPHY_API_KEY"
|
|
6
|
+
|
|
7
|
+
def initialize(api_key = nil)
|
|
8
|
+
api_key ||= ENV.fetch(API_KEY_NAME)
|
|
9
|
+
@request = Net::Request.new(api_key)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def globaldem(south:, north:, west:, east:, demtype: :srtmgl3, output_format: :tif)
|
|
13
|
+
params = Net::Params::GlobaldemParams.new(south:, north:, west:, east:, demtype:, output_format:)
|
|
14
|
+
params.validate!
|
|
15
|
+
|
|
16
|
+
response = request.globaldem(params)
|
|
17
|
+
|
|
18
|
+
if response.success?
|
|
19
|
+
Services::ResponseToDemConverter.call(response, dem_type: demtype, output_format:)
|
|
20
|
+
else
|
|
21
|
+
raise ::OpenTopo::Errors::ResponseError, response.body
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def usgsdem(south:, north:, west:, east:, dataset_name: :usgs10m, output_format: :tif)
|
|
26
|
+
params = Net::Params::UsgsdemParams.new(south:, north:, west:, east:, dataset_name:, output_format:)
|
|
27
|
+
params.validate!
|
|
28
|
+
|
|
29
|
+
response = request.usgsdem(params)
|
|
30
|
+
|
|
31
|
+
if response.success?
|
|
32
|
+
Services::ResponseToDemConverter.call(response, dem_type: dataset_name, output_format:)
|
|
33
|
+
else
|
|
34
|
+
raise ::OpenTopo::Errors::ResponseError, response.body
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def elevation(long:, lat:, dataset: :srtmgl3)
|
|
39
|
+
params = Net::Params::ElevationParams.new(long:, lat:, dataset:)
|
|
40
|
+
params.validate!
|
|
41
|
+
|
|
42
|
+
response = request.elevation(params)
|
|
43
|
+
|
|
44
|
+
if response.success?
|
|
45
|
+
Services::ResponseToPointConverter.call(response)
|
|
46
|
+
else
|
|
47
|
+
raise ::OpenTopo::Errors::ResponseError, response.body
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def catalog(west: nil, south: nil, east: nil, north: nil, polygon: nil)
|
|
52
|
+
params = Net::Params::CatalogParams.new(west:, south:, east:, north:, polygon:)
|
|
53
|
+
params.validate!
|
|
54
|
+
|
|
55
|
+
response = request.catalog(params)
|
|
56
|
+
|
|
57
|
+
if response.success?
|
|
58
|
+
Services::ResponseToCatalogListConverter.call(response.body)
|
|
59
|
+
else
|
|
60
|
+
raise ::OpenTopo::Errors::ResponseError, response.body
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
attr_reader :request
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module OpenTopo
|
|
5
|
+
class DemFile
|
|
6
|
+
attr_reader :original_file
|
|
7
|
+
alias_method :file, :original_file
|
|
8
|
+
|
|
9
|
+
attr_reader :dem_type
|
|
10
|
+
|
|
11
|
+
attr_reader :output_format
|
|
12
|
+
|
|
13
|
+
def initialize(original_file:, dem_type:, output_format:)
|
|
14
|
+
@original_file = original_file
|
|
15
|
+
@dem_type = dem_type
|
|
16
|
+
@output_format = output_format
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def convert_to(format, file_path: nil)
|
|
20
|
+
Services::DemConverter.call(original_file, format, file_path: file_path)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenTopo
|
|
4
|
+
module Errors
|
|
5
|
+
class FileError < BaseError
|
|
6
|
+
attr_reader :file_path
|
|
7
|
+
|
|
8
|
+
def initialize(message = "", file_path: nil)
|
|
9
|
+
super(message)
|
|
10
|
+
@file_path = file_path
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def file
|
|
14
|
+
return nil if @file_path.nil?
|
|
15
|
+
|
|
16
|
+
File.open(@file_path)
|
|
17
|
+
rescue Errno::ENOENT
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "faraday"
|
|
4
|
+
require "faraday_middleware"
|
|
5
|
+
require "oj"
|
|
6
|
+
|
|
7
|
+
module OpenTopo
|
|
8
|
+
module Net
|
|
9
|
+
class Connection
|
|
10
|
+
def initialize(api_key = nil)
|
|
11
|
+
@api_key = api_key
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def build
|
|
15
|
+
create_connection
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
BASE_URL = "https://portal.opentopography.org"
|
|
21
|
+
XML_CONTENT_TYPE = "application/xml"
|
|
22
|
+
ACCEPT_CONTENT_TYPE = "application/octet-stream,application/json,#{XML_CONTENT_TYPE}"
|
|
23
|
+
|
|
24
|
+
attr_reader :api_key
|
|
25
|
+
|
|
26
|
+
def create_connection
|
|
27
|
+
Faraday.new(options, request: request_options) do |faraday|
|
|
28
|
+
faraday.request :url_encoded
|
|
29
|
+
faraday.response :logger
|
|
30
|
+
faraday.response :xml, content_type: XML_CONTENT_TYPE
|
|
31
|
+
faraday.response :json, parser_options: {decoder: Oj, symbolize_names: true}
|
|
32
|
+
faraday.adapter Faraday.default_adapter
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def options
|
|
37
|
+
{
|
|
38
|
+
url: BASE_URL,
|
|
39
|
+
headers: headers,
|
|
40
|
+
params: {API_Key: api_key}
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def headers
|
|
45
|
+
{
|
|
46
|
+
"Accept" => ACCEPT_CONTENT_TYPE,
|
|
47
|
+
"User-Agent" => "Ruby open_topo v#{OpenTopo::VERSION}"
|
|
48
|
+
}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def request_options
|
|
52
|
+
{
|
|
53
|
+
# timeout: 7 # it creates tif file, so it can be slow :(
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenTopo
|
|
4
|
+
module Net
|
|
5
|
+
module Params
|
|
6
|
+
class BaseParams
|
|
7
|
+
extend Forwardable
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@validation_result = nil
|
|
11
|
+
|
|
12
|
+
if self.class.contract.nil?
|
|
13
|
+
raise NotImplementedError, "Contract not configured"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_params
|
|
18
|
+
raise NotImplementedError, "Method to_params not implemented"
|
|
19
|
+
end
|
|
20
|
+
def_delegator :self, :to_params, :to_h
|
|
21
|
+
|
|
22
|
+
def valid?
|
|
23
|
+
validate
|
|
24
|
+
validation_result.success?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def validate!
|
|
28
|
+
if !valid?
|
|
29
|
+
raise ::OpenTopo::Errors::ParamsError, "Invalid parameters: #{error_messages.join(", ")}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def error_messages
|
|
36
|
+
if !validation_result.nil? && !validation_result.success?
|
|
37
|
+
validation_result.errors(full: true).messages.map(&:text)
|
|
38
|
+
else
|
|
39
|
+
[]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
attr_reader :validation_result
|
|
46
|
+
|
|
47
|
+
def validate
|
|
48
|
+
@validation_result = self.class.contract.new.call(to_params)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class << self
|
|
52
|
+
attr_reader :contract
|
|
53
|
+
|
|
54
|
+
def validate_with(contract)
|
|
55
|
+
@contract = contract
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenTopo
|
|
4
|
+
module Net
|
|
5
|
+
module Params
|
|
6
|
+
class CatalogParams < BaseParams
|
|
7
|
+
validate_with Contracts::CatalogContract
|
|
8
|
+
|
|
9
|
+
attr_reader :west, :south, :east, :north, :polygon
|
|
10
|
+
|
|
11
|
+
def initialize(west: nil, south: nil, east: nil, north: nil, polygon: nil)
|
|
12
|
+
@west = west
|
|
13
|
+
@south = south
|
|
14
|
+
@east = east
|
|
15
|
+
@north = north
|
|
16
|
+
@polygon = polygon
|
|
17
|
+
|
|
18
|
+
super()
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_params
|
|
22
|
+
params = default_params
|
|
23
|
+
|
|
24
|
+
if polygon
|
|
25
|
+
params[:polygon] = polygon
|
|
26
|
+
else
|
|
27
|
+
params[:maxx] = east
|
|
28
|
+
params[:minx] = west
|
|
29
|
+
|
|
30
|
+
params[:maxy] = north
|
|
31
|
+
params[:miny] = south
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
params
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def default_params
|
|
40
|
+
{
|
|
41
|
+
productFormat: "Raster",
|
|
42
|
+
outputFormat: "xml",
|
|
43
|
+
detail: false,
|
|
44
|
+
include_federated: false
|
|
45
|
+
}
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry-validation"
|
|
4
|
+
|
|
5
|
+
module OpenTopo
|
|
6
|
+
module Net
|
|
7
|
+
module Params
|
|
8
|
+
module Contracts
|
|
9
|
+
class CatalogContract < ::Dry::Validation::Contract
|
|
10
|
+
COORDS = {
|
|
11
|
+
minx: :west,
|
|
12
|
+
miny: :south,
|
|
13
|
+
maxx: :east,
|
|
14
|
+
maxy: :north
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
params do
|
|
18
|
+
optional(:minx).filled(:float, gteq?: -180, lteq?: 180)
|
|
19
|
+
optional(:miny).filled(:float, gteq?: -90, lteq?: 90)
|
|
20
|
+
optional(:maxx).filled(:float, gteq?: -180, lteq?: 180)
|
|
21
|
+
optional(:maxy).filled(:float, gteq?: -90, lteq?: 90)
|
|
22
|
+
optional(:polygon).filled(:string)
|
|
23
|
+
|
|
24
|
+
required(:productFormat).filled(:string)
|
|
25
|
+
required(:outputFormat).filled(:string)
|
|
26
|
+
required(:detail).filled(:bool)
|
|
27
|
+
required(:include_federated).filled(:bool)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
rule(:polygon, :minx, :miny, :maxx, :maxy) do
|
|
31
|
+
next if !values[:polygon].nil?
|
|
32
|
+
|
|
33
|
+
if COORDS.keys.any? { |key| values[key].nil? }
|
|
34
|
+
key(:base).failure("either polygon or all bounding box coordinates (#{COORDS.values.join(", ")}) must be provided")
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
rule(:minx, :maxx) do
|
|
39
|
+
next if values[:polygon]
|
|
40
|
+
next if values[:minx].nil? || values[:maxx].nil?
|
|
41
|
+
|
|
42
|
+
key(COORDS[:minx]).failure("must be less than #{COORDS[:maxx]} (#{values[:maxx]})") if values[:minx] >= values[:maxx]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
rule(:miny, :maxy) do
|
|
46
|
+
next if values[:polygon]
|
|
47
|
+
next if values[:miny].nil? || values[:maxy].nil?
|
|
48
|
+
|
|
49
|
+
key(COORDS[:miny]).failure("must be less than #{COORDS[:maxy]} (#{values[:maxy]})") if values[:miny] >= values[:maxy]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
rule(:minx, :miny, :maxx, :maxy) do
|
|
53
|
+
next if values[:polygon]
|
|
54
|
+
|
|
55
|
+
missing = COORDS.keys.select { |key| values[key].nil? }
|
|
56
|
+
next if missing.size == COORDS.size
|
|
57
|
+
|
|
58
|
+
missing.each do |key, value|
|
|
59
|
+
key(COORDS[key]).failure("must be provided") if value.nil?
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
rule(:polygon) do
|
|
64
|
+
next if value.nil?
|
|
65
|
+
|
|
66
|
+
coords = value.split(",")
|
|
67
|
+
if coords.size.odd?
|
|
68
|
+
key.failure("must consists of pairs of (longitude, latitude)")
|
|
69
|
+
next
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
coords.map(&:to_f).each_slice(2).with_index do |(long, lat), index|
|
|
73
|
+
key.failure("#{index} point - longitude must be less than or equal to 180") if long > 180
|
|
74
|
+
key.failure("#{index} point - latitude must be less than or equal to 90") if lat > 90
|
|
75
|
+
|
|
76
|
+
key.failure("#{index} point - longitude must be greater than or equal to -180") if long < -180
|
|
77
|
+
key.failure("#{index} point - latitude must be greater than or equal to -90") if lat < -90
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry-validation"
|
|
4
|
+
|
|
5
|
+
module OpenTopo
|
|
6
|
+
module Net
|
|
7
|
+
module Params
|
|
8
|
+
module Contracts
|
|
9
|
+
class ElevationContract < ::Dry::Validation::Contract
|
|
10
|
+
params do
|
|
11
|
+
required(:longitude).filled(:float, gteq?: -180, lteq?: 180)
|
|
12
|
+
required(:latitude).filled(:float, gteq?: -90, lteq?: 90)
|
|
13
|
+
required(:dataset).filled(:string)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
rule(:dataset) do
|
|
17
|
+
key.failure("must be a valid dataset name") unless Params::ElevationParams::DATASETS.value?(value)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry-validation"
|
|
4
|
+
|
|
5
|
+
module OpenTopo
|
|
6
|
+
module Net
|
|
7
|
+
module Params
|
|
8
|
+
module Contracts
|
|
9
|
+
class GlobaldemContract < ::Dry::Validation::Contract
|
|
10
|
+
params do
|
|
11
|
+
required(:south).filled(:float, gt?: -90, lt?: 90)
|
|
12
|
+
required(:north).filled(:float, gt?: -90, lt?: 90)
|
|
13
|
+
required(:west).filled(:float, gt?: -180, lt?: 180)
|
|
14
|
+
required(:east).filled(:float, gt?: -180, lt?: 180)
|
|
15
|
+
required(:demtype).filled(:string)
|
|
16
|
+
required(:outputFormat).filled(:string)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
rule(:south, :north) do
|
|
20
|
+
key(:south).failure("must be less than north (#{values[:north]})") if values[:south] >= values[:north]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
rule(:west, :east) do
|
|
24
|
+
key(:west).failure("must be less than east (#{values[:east]})") if values[:west] >= values[:east]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
rule(:demtype) do
|
|
28
|
+
key.failure("must be a valid DEM type") unless Params::GlobaldemParams::DEM_TYPES.value?(value)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
rule(:outputFormat) do
|
|
32
|
+
key.failure("must be a valid output format") unless Params::GlobaldemParams::OUTPUT_FORMATS.value?(value)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry-validation"
|
|
4
|
+
|
|
5
|
+
module OpenTopo
|
|
6
|
+
module Net
|
|
7
|
+
module Params
|
|
8
|
+
module Contracts
|
|
9
|
+
class UsgsdemContract < ::Dry::Validation::Contract
|
|
10
|
+
params do
|
|
11
|
+
required(:south).filled(:float, gt?: -90, lt?: 90)
|
|
12
|
+
required(:north).filled(:float, gt?: -90, lt?: 90)
|
|
13
|
+
required(:west).filled(:float, gt?: -180, lt?: 180)
|
|
14
|
+
required(:east).filled(:float, gt?: -180, lt?: 180)
|
|
15
|
+
required(:datasetName).filled(:string)
|
|
16
|
+
required(:outputFormat).filled(:string)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
rule(:south, :north) do
|
|
20
|
+
key(:south).failure("must be less than north (#{values[:north]})") if values[:south] >= values[:north]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
rule(:west, :east) do
|
|
24
|
+
key(:west).failure("must be less than east (#{values[:east]})") if values[:west] >= values[:east]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
rule(:datasetName) do
|
|
28
|
+
key.failure("must be a valid DEM type") unless Params::UsgsdemParams::DEM_TYPES.value?(value)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
rule(:outputFormat) do
|
|
32
|
+
key.failure("must be a valid output format") unless Params::UsgsdemParams::OUTPUT_FORMATS.value?(value)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenTopo
|
|
4
|
+
module Net
|
|
5
|
+
module Params
|
|
6
|
+
class ElevationParams < BaseParams
|
|
7
|
+
DATASETS = {
|
|
8
|
+
anadem: "ANADEM", # (DTM 30m)
|
|
9
|
+
arctic_dem10m: "ArcticDEM10m",
|
|
10
|
+
arctic_dem2m: "ArcticDEM2m",
|
|
11
|
+
arctic_dem32m: "ArcticDEM32m",
|
|
12
|
+
aw3_d30_e: "AW3D30_E", # (ALOS World 3D Ellipsoidal, 30m)
|
|
13
|
+
aw3_d30: "AW3D30", # (ALOS World 3D 30m)
|
|
14
|
+
ca_mrdem_dsm: "CA_MRDEM_DSM", # (DSM 30m)
|
|
15
|
+
ca_mrdem_dtm: "CA_MRDEM_DTM", # (DTM 30m)
|
|
16
|
+
cop30: "COP30", # (Copernicus Global DSM 30m)
|
|
17
|
+
cop90: "COP90", # (Copernicus Global DSM 90m)
|
|
18
|
+
eu_dtm: "EU_DTM", # (DTM 30m)
|
|
19
|
+
gebco_ice_topo: "GEBCOIceTopo", # (Global Bathymetry 500m)
|
|
20
|
+
gebco_sub_ice_topo: "GEBCOSubIceTopo", # (Global Bathymetry 500m)
|
|
21
|
+
gedi_l3: "GEDI_L3", # (DTM 1000m)
|
|
22
|
+
gedtm30: "GEDTM30", # (DTM 30m)
|
|
23
|
+
linz1m_dsm: "LINZ1m_DSM",
|
|
24
|
+
linz1m_dtm: "LINZ1m_DTM",
|
|
25
|
+
nasadem: "NASADEM", # (NASADEM Global DEM)
|
|
26
|
+
rema10m: "REMA10m",
|
|
27
|
+
rema2m: "REMA2m",
|
|
28
|
+
rema32m: "REMA32m",
|
|
29
|
+
srtm15_plus: "SRTM15Plus", # (Global Bathymetry SRTM15+ V2.1 500m)
|
|
30
|
+
srtmgl1_e: "SRTM_GL1_Ellip", # (SRTM GL1 Ellipsoidal 30m)
|
|
31
|
+
srtmgl1: "SRTM_GL1", # (SRTM GL1 30m)
|
|
32
|
+
srtmgl3: "SRTM_GL3", # (SRTM GL3 90m)
|
|
33
|
+
usgs10m: "USGS10m",
|
|
34
|
+
usgs30m: "USGS30m"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
validate_with Contracts::ElevationContract
|
|
38
|
+
|
|
39
|
+
attr_reader :long, :lat, :dataset
|
|
40
|
+
|
|
41
|
+
def initialize(long:, lat:, dataset: :cop30)
|
|
42
|
+
@long = long
|
|
43
|
+
@lat = lat
|
|
44
|
+
@dataset = dataset
|
|
45
|
+
|
|
46
|
+
super()
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def to_params
|
|
50
|
+
{
|
|
51
|
+
longitude: long,
|
|
52
|
+
latitude: lat,
|
|
53
|
+
dataset: DATASETS[dataset.to_sym]
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenTopo
|
|
4
|
+
module Net
|
|
5
|
+
module Params
|
|
6
|
+
class GlobaldemParams < BaseParams
|
|
7
|
+
OUTPUT_FORMATS = {tif: "GTiff", hfa: "HFA", asc: "AAIGrid"}
|
|
8
|
+
DEM_TYPES = {
|
|
9
|
+
anadem: "ANADEM", # (DTM 30m)
|
|
10
|
+
aw3_d30_e: "AW3D30_E", # (ALOS World 3D Ellipsoidal, 30m)
|
|
11
|
+
aw3_d30: "AW3D30", # (ALOS World 3D 30m)
|
|
12
|
+
ca_mrdem_dsm: "CA_MRDEM_DSM", # (DSM 30m)
|
|
13
|
+
ca_mrdem_dtm: "CA_MRDEM_DTM", # (DTM 30m)
|
|
14
|
+
cop30: "COP30", # (Copernicus Global DSM 30m)
|
|
15
|
+
cop90: "COP90", # (Copernicus Global DSM 90m)
|
|
16
|
+
eu_dtm: "EU_DTM", # (DTM 30m)
|
|
17
|
+
gebco_ice_topo: "GEBCOIceTopo", # (Global Bathymetry 500m)
|
|
18
|
+
gebco_sub_ice_topo: "GEBCOSubIceTopo", # (Global Bathymetry 500m)
|
|
19
|
+
gedi_l3: "GEDI_L3", # (DTM 1000m)
|
|
20
|
+
gedtm30: "GEDTM30", # (DTM 30m)
|
|
21
|
+
nasadem: "NASADEM", # (NASADEM Global DEM)
|
|
22
|
+
srtm15_plus: "SRTM15Plus", # (Global Bathymetry SRTM15+ V2.1 500m)
|
|
23
|
+
srtmgl1_e: "SRTMGL1_E", # (SRTM GL1 Ellipsoidal 30m)
|
|
24
|
+
srtmgl1: "SRTMGL1", # (SRTM GL1 30m)
|
|
25
|
+
srtmgl3: "SRTMGL3" # (SRTM GL3 90m)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
validate_with Contracts::GlobaldemContract
|
|
29
|
+
|
|
30
|
+
attr_reader :south, :north, :west, :east, :demtype, :output_format
|
|
31
|
+
|
|
32
|
+
def initialize(south:, north:, west:, east:, demtype:, output_format:)
|
|
33
|
+
@south = south
|
|
34
|
+
@north = north
|
|
35
|
+
@west = west
|
|
36
|
+
@east = east
|
|
37
|
+
@demtype = demtype
|
|
38
|
+
@output_format = output_format
|
|
39
|
+
|
|
40
|
+
super()
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def to_params
|
|
44
|
+
{
|
|
45
|
+
south:,
|
|
46
|
+
north:,
|
|
47
|
+
west:,
|
|
48
|
+
east:,
|
|
49
|
+
demtype: DEM_TYPES[demtype.to_sym],
|
|
50
|
+
outputFormat: OUTPUT_FORMATS[output_format.to_sym]
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenTopo
|
|
4
|
+
module Net
|
|
5
|
+
module Params
|
|
6
|
+
class UsgsdemParams < BaseParams
|
|
7
|
+
OUTPUT_FORMATS = {tif: "GTiff", hfa: "HFA", asc: "AAIGrid"}
|
|
8
|
+
DEM_TYPES = {
|
|
9
|
+
usgs1m: "USGS1m",
|
|
10
|
+
usgs10m: "USGS10m",
|
|
11
|
+
usgs30m: "USGS30m"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
validate_with Contracts::UsgsdemContract
|
|
15
|
+
|
|
16
|
+
attr_reader :south, :north, :west, :east, :demtype, :output_format
|
|
17
|
+
|
|
18
|
+
def initialize(south:, north:, west:, east:, dataset_name:, output_format:)
|
|
19
|
+
@south = south
|
|
20
|
+
@north = north
|
|
21
|
+
@west = west
|
|
22
|
+
@east = east
|
|
23
|
+
@demtype = dataset_name
|
|
24
|
+
@output_format = output_format
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def to_params
|
|
28
|
+
{
|
|
29
|
+
south:,
|
|
30
|
+
north:,
|
|
31
|
+
west:,
|
|
32
|
+
east:,
|
|
33
|
+
datasetName: DEM_TYPES[demtype.to_sym],
|
|
34
|
+
outputFormat: OUTPUT_FORMATS[output_format.to_sym]
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenTopo
|
|
4
|
+
module Net
|
|
5
|
+
class Request
|
|
6
|
+
GLOBALDEM_ENDPOINT = "/API/globaldem"
|
|
7
|
+
USGSDEM_ENDPOINT = "/API/usgsdem"
|
|
8
|
+
ELEVATION_ENDPOINT = "/API/v1/elevation"
|
|
9
|
+
CATALOG_ENDPOINT = "/API/otCatalog"
|
|
10
|
+
|
|
11
|
+
def initialize(api_key = nil)
|
|
12
|
+
@connection = Connection.new(api_key).build
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def globaldem(params)
|
|
16
|
+
Response.new(@connection.get(GLOBALDEM_ENDPOINT, params.to_params))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def usgsdem(params)
|
|
20
|
+
Response.new(@connection.get(USGSDEM_ENDPOINT, params.to_params))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def elevation(params)
|
|
24
|
+
Response.new(@connection.get(ELEVATION_ENDPOINT, params.to_params))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def catalog(params)
|
|
28
|
+
Response.new(@connection.get(CATALOG_ENDPOINT, params.to_params))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenTopo
|
|
4
|
+
module Net
|
|
5
|
+
class Response
|
|
6
|
+
NOT_FOUND_MESSAGE = "Not found"
|
|
7
|
+
NO_DATA_MESSAGE = "No data"
|
|
8
|
+
|
|
9
|
+
attr_reader :body, :headers, :status
|
|
10
|
+
|
|
11
|
+
Headers = Struct.new(:date, :content_disposition, :content_length, :content_type) do
|
|
12
|
+
def filename
|
|
13
|
+
if content_disposition.nil? || !content_disposition.match?(filename_header)
|
|
14
|
+
return nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
File.basename(content_disposition.split(filename_header).last.delete('"'), ".*")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def filename_header = "filename="
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def initialize(response)
|
|
26
|
+
@body = set_body_message(response)
|
|
27
|
+
@headers = parse_headers(response.headers)
|
|
28
|
+
@status = response.status
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def success?
|
|
32
|
+
(200..299).cover?(status)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def failure?
|
|
36
|
+
!success?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def data?
|
|
40
|
+
success? && status != 204
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def parse_headers(headers)
|
|
46
|
+
Headers.new(
|
|
47
|
+
headers["Date"],
|
|
48
|
+
headers["Content-Disposition"],
|
|
49
|
+
headers["Content-Length"],
|
|
50
|
+
headers["Content-Type"]
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def set_body_message(response)
|
|
55
|
+
case response.status
|
|
56
|
+
when 204 then NO_DATA_MESSAGE
|
|
57
|
+
when 404 then NOT_FOUND_MESSAGE
|
|
58
|
+
else
|
|
59
|
+
response.body
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module OpenTopo
|
|
5
|
+
class Point
|
|
6
|
+
attr_reader :long
|
|
7
|
+
alias_method :longitude, :long
|
|
8
|
+
|
|
9
|
+
attr_reader :lat
|
|
10
|
+
alias_method :latitude, :lat
|
|
11
|
+
|
|
12
|
+
attr_reader :heigh
|
|
13
|
+
alias_method :elevation, :heigh
|
|
14
|
+
|
|
15
|
+
attr_reader :unit
|
|
16
|
+
|
|
17
|
+
def initialize(long:, lat:, heigh: nil, unit: "m")
|
|
18
|
+
@long = long
|
|
19
|
+
@lat = lat
|
|
20
|
+
@heigh = heigh
|
|
21
|
+
@unit = unit
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "open3"
|
|
5
|
+
|
|
6
|
+
module OpenTopo
|
|
7
|
+
module Services
|
|
8
|
+
class DemConverter
|
|
9
|
+
FILENAME_EXT_SPLITTER = ".*"
|
|
10
|
+
|
|
11
|
+
def self.call(file, file_format, file_path: nil)
|
|
12
|
+
if file.nil? || !File.file?(file)
|
|
13
|
+
raise OpenTopo::Errors::FileError.new("File is not a file (#{file.class})", file_path: file)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
new_filename = build_new_filename(file, file_path, file_format)
|
|
17
|
+
original_filename = file.path
|
|
18
|
+
|
|
19
|
+
_message, error, status = Open3.capture3(gdal_translate_command(original_filename, new_filename))
|
|
20
|
+
|
|
21
|
+
if status.success?
|
|
22
|
+
File.open(new_filename, "r")
|
|
23
|
+
else
|
|
24
|
+
raise OpenTopo::Errors::ConvertError, error.strip
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class << self
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def build_new_filename(file, file_path, file_format)
|
|
32
|
+
extname = File.extname(file_path.to_s)
|
|
33
|
+
ext = extname.empty? ? ".#{file_format}" : extname
|
|
34
|
+
file_path = file.path if file_path.nil? || file_path.empty?
|
|
35
|
+
|
|
36
|
+
if !File.directory?(file_path) && File.extname(file_path).empty?
|
|
37
|
+
raise OpenTopo::Errors::FileError.new("No such file or directory", file_path:)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
path, filename =
|
|
41
|
+
if File.directory?(file_path.to_s)
|
|
42
|
+
[file_path, File.basename(file.path, FILENAME_EXT_SPLITTER)]
|
|
43
|
+
else
|
|
44
|
+
[File.dirname(file_path), File.basename(file_path, FILENAME_EXT_SPLITTER)]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
File.join(path, "#{filename}#{ext}")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def gdal_translate_command(original_filename, new_filename)
|
|
51
|
+
"gdal_translate -of XYZ -co COLUMN_SEPARATOR=, -co ADD_HEADER_LINE=YES #{original_filename} #{new_filename}"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenTopo
|
|
4
|
+
module Services
|
|
5
|
+
class ResponseToCatalogListConverter
|
|
6
|
+
def self.call(data)
|
|
7
|
+
datasets = data&.dig("Datasets", "Dataset")
|
|
8
|
+
return [] if datasets.nil?
|
|
9
|
+
|
|
10
|
+
datasets.map do |dataset|
|
|
11
|
+
created_at = dataset["temporalCoverage"].split(" / ").map { |string_date| Date.parse(string_date) }
|
|
12
|
+
|
|
13
|
+
OpenTopo::Catalog.new(
|
|
14
|
+
full_name: dataset["name"],
|
|
15
|
+
name: dataset["alternateName"],
|
|
16
|
+
url: dataset["url"],
|
|
17
|
+
date_created: dataset["dateCreated"],
|
|
18
|
+
temporal_coverage: {from: created_at[0], to: created_at[1]},
|
|
19
|
+
unit: dataset["spatialCoverage"]["additionalProperty"].find { |p| p["name"] == "Unit" }&.fetch("value")
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenTopo
|
|
4
|
+
module Services
|
|
5
|
+
class ResponseToDemConverter
|
|
6
|
+
DATE_TIME_FORMAT = "%F-%H-%M-%S"
|
|
7
|
+
|
|
8
|
+
def self.call(response, dem_type:, output_format:)
|
|
9
|
+
DemFile.new(original_file: file_from_response(response), dem_type:, output_format:)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def file_from_response(response)
|
|
16
|
+
return nil if !response.data?
|
|
17
|
+
|
|
18
|
+
filename = filename_from_response(response)
|
|
19
|
+
file = File.new([filename, ".tif"].join, "w")
|
|
20
|
+
|
|
21
|
+
file.binmode
|
|
22
|
+
file.write(response.body)
|
|
23
|
+
file.close
|
|
24
|
+
|
|
25
|
+
File.open(file, "r")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def filename_from_response(response)
|
|
29
|
+
response.headers.filename || "dem_#{Time.now.strftime(DATE_TIME_FORMAT)}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenTopo
|
|
4
|
+
module Services
|
|
5
|
+
class ResponseToPointConverter
|
|
6
|
+
def self.call(response)
|
|
7
|
+
Point.new(
|
|
8
|
+
long: response.body[:Location][:Longitude],
|
|
9
|
+
lat: response.body[:Location][:Latitude],
|
|
10
|
+
heigh: response.body[:Elevation],
|
|
11
|
+
unit: response.body[:Unit]
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/open_topo.rb
ADDED
data/sig/open_topo.rbs
ADDED
metadata
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: open_topo
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- andrew-kh8
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: dry-validation
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.11'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.11'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: faraday_middleware
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.2'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.2'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: faraday
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '1.10'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '1.10'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: multi_xml
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0.9'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0.9'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: oj
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '3.17'
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '3.17'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: ox
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '2.14'
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '2.14'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: zeitwerk
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '0'
|
|
103
|
+
type: :runtime
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: bundle-audit
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '0'
|
|
117
|
+
type: :development
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '0'
|
|
124
|
+
- !ruby/object:Gem::Dependency
|
|
125
|
+
name: factory_bot
|
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - "~>"
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: '6.6'
|
|
131
|
+
type: :development
|
|
132
|
+
prerelease: false
|
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - "~>"
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '6.6'
|
|
138
|
+
- !ruby/object:Gem::Dependency
|
|
139
|
+
name: fasterer
|
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
|
141
|
+
requirements:
|
|
142
|
+
- - "~>"
|
|
143
|
+
- !ruby/object:Gem::Version
|
|
144
|
+
version: '0.11'
|
|
145
|
+
type: :development
|
|
146
|
+
prerelease: false
|
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
148
|
+
requirements:
|
|
149
|
+
- - "~>"
|
|
150
|
+
- !ruby/object:Gem::Version
|
|
151
|
+
version: '0.11'
|
|
152
|
+
- !ruby/object:Gem::Dependency
|
|
153
|
+
name: rspec
|
|
154
|
+
requirement: !ruby/object:Gem::Requirement
|
|
155
|
+
requirements:
|
|
156
|
+
- - "~>"
|
|
157
|
+
- !ruby/object:Gem::Version
|
|
158
|
+
version: '3.0'
|
|
159
|
+
type: :development
|
|
160
|
+
prerelease: false
|
|
161
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
162
|
+
requirements:
|
|
163
|
+
- - "~>"
|
|
164
|
+
- !ruby/object:Gem::Version
|
|
165
|
+
version: '3.0'
|
|
166
|
+
- !ruby/object:Gem::Dependency
|
|
167
|
+
name: simplecov
|
|
168
|
+
requirement: !ruby/object:Gem::Requirement
|
|
169
|
+
requirements:
|
|
170
|
+
- - "~>"
|
|
171
|
+
- !ruby/object:Gem::Version
|
|
172
|
+
version: 1.1.1
|
|
173
|
+
type: :development
|
|
174
|
+
prerelease: false
|
|
175
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
176
|
+
requirements:
|
|
177
|
+
- - "~>"
|
|
178
|
+
- !ruby/object:Gem::Version
|
|
179
|
+
version: 1.1.1
|
|
180
|
+
- !ruby/object:Gem::Dependency
|
|
181
|
+
name: standard
|
|
182
|
+
requirement: !ruby/object:Gem::Requirement
|
|
183
|
+
requirements:
|
|
184
|
+
- - "~>"
|
|
185
|
+
- !ruby/object:Gem::Version
|
|
186
|
+
version: '1.3'
|
|
187
|
+
type: :development
|
|
188
|
+
prerelease: false
|
|
189
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
190
|
+
requirements:
|
|
191
|
+
- - "~>"
|
|
192
|
+
- !ruby/object:Gem::Version
|
|
193
|
+
version: '1.3'
|
|
194
|
+
- !ruby/object:Gem::Dependency
|
|
195
|
+
name: timecop
|
|
196
|
+
requirement: !ruby/object:Gem::Requirement
|
|
197
|
+
requirements:
|
|
198
|
+
- - "~>"
|
|
199
|
+
- !ruby/object:Gem::Version
|
|
200
|
+
version: '0.9'
|
|
201
|
+
type: :development
|
|
202
|
+
prerelease: false
|
|
203
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
204
|
+
requirements:
|
|
205
|
+
- - "~>"
|
|
206
|
+
- !ruby/object:Gem::Version
|
|
207
|
+
version: '0.9'
|
|
208
|
+
- !ruby/object:Gem::Dependency
|
|
209
|
+
name: vcr
|
|
210
|
+
requirement: !ruby/object:Gem::Requirement
|
|
211
|
+
requirements:
|
|
212
|
+
- - "~>"
|
|
213
|
+
- !ruby/object:Gem::Version
|
|
214
|
+
version: '6.4'
|
|
215
|
+
type: :development
|
|
216
|
+
prerelease: false
|
|
217
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
218
|
+
requirements:
|
|
219
|
+
- - "~>"
|
|
220
|
+
- !ruby/object:Gem::Version
|
|
221
|
+
version: '6.4'
|
|
222
|
+
description: A simple Ruby client for interacting with the Open Topography API.
|
|
223
|
+
email:
|
|
224
|
+
- horolskyandrey@gmail.com
|
|
225
|
+
executables: []
|
|
226
|
+
extensions: []
|
|
227
|
+
extra_rdoc_files: []
|
|
228
|
+
files:
|
|
229
|
+
- CHANGELOG.md
|
|
230
|
+
- CODE_OF_CONDUCT.md
|
|
231
|
+
- LICENSE.txt
|
|
232
|
+
- Rakefile
|
|
233
|
+
- lib/open_topo.rb
|
|
234
|
+
- lib/open_topo/catalog.rb
|
|
235
|
+
- lib/open_topo/client.rb
|
|
236
|
+
- lib/open_topo/dem_file.rb
|
|
237
|
+
- lib/open_topo/errors/base_error.rb
|
|
238
|
+
- lib/open_topo/errors/convert_error.rb
|
|
239
|
+
- lib/open_topo/errors/file_error.rb
|
|
240
|
+
- lib/open_topo/errors/params_error.rb
|
|
241
|
+
- lib/open_topo/errors/response_error.rb
|
|
242
|
+
- lib/open_topo/net/connection.rb
|
|
243
|
+
- lib/open_topo/net/params/base_params.rb
|
|
244
|
+
- lib/open_topo/net/params/catalog_params.rb
|
|
245
|
+
- lib/open_topo/net/params/contracts/catalog_contract.rb
|
|
246
|
+
- lib/open_topo/net/params/contracts/elevation_contract.rb
|
|
247
|
+
- lib/open_topo/net/params/contracts/globaldem_contract.rb
|
|
248
|
+
- lib/open_topo/net/params/contracts/usgsdem_contract.rb
|
|
249
|
+
- lib/open_topo/net/params/elevation_params.rb
|
|
250
|
+
- lib/open_topo/net/params/globaldem_params.rb
|
|
251
|
+
- lib/open_topo/net/params/usgsdem_params.rb
|
|
252
|
+
- lib/open_topo/net/request.rb
|
|
253
|
+
- lib/open_topo/net/response.rb
|
|
254
|
+
- lib/open_topo/point.rb
|
|
255
|
+
- lib/open_topo/services/dem_converter.rb
|
|
256
|
+
- lib/open_topo/services/response_to_catalog_list_converter.rb
|
|
257
|
+
- lib/open_topo/services/response_to_dem_converter.rb
|
|
258
|
+
- lib/open_topo/services/response_to_point_converter.rb
|
|
259
|
+
- lib/open_topo/version.rb
|
|
260
|
+
- sig/open_topo.rbs
|
|
261
|
+
homepage: https://github.com/andrew-kh8/open_topo
|
|
262
|
+
licenses:
|
|
263
|
+
- MIT
|
|
264
|
+
metadata:
|
|
265
|
+
allowed_push_host: https://rubygems.org
|
|
266
|
+
homepage_uri: https://github.com/andrew-kh8/open_topo
|
|
267
|
+
source_code_uri: https://github.com/andrew-kh8/open_topo
|
|
268
|
+
changelog_uri: https://github.com/andrew-kh8/open_topo/blob/master/CHANGELOG.md
|
|
269
|
+
rdoc_options: []
|
|
270
|
+
require_paths:
|
|
271
|
+
- lib
|
|
272
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
273
|
+
requirements:
|
|
274
|
+
- - ">="
|
|
275
|
+
- !ruby/object:Gem::Version
|
|
276
|
+
version: 3.3.6
|
|
277
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
278
|
+
requirements:
|
|
279
|
+
- - ">="
|
|
280
|
+
- !ruby/object:Gem::Version
|
|
281
|
+
version: '0'
|
|
282
|
+
requirements: []
|
|
283
|
+
rubygems_version: 4.0.18
|
|
284
|
+
specification_version: 4
|
|
285
|
+
summary: Ruby Open Topography client
|
|
286
|
+
test_files: []
|