cdss-ruby 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/.rubocop.yml +86 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/README.md +101 -0
- data/Rakefile +26 -0
- data/docs/Cdss/AdminCalls.html +399 -0
- data/docs/Cdss/Analysis.html +972 -0
- data/docs/Cdss/Client.html +581 -0
- data/docs/Cdss/Climate.html +1257 -0
- data/docs/Cdss/Concerns/LogReadingAttributes.html +406 -0
- data/docs/Cdss/Concerns/WellReadingAttributes.html +414 -0
- data/docs/Cdss/Concerns.html +117 -0
- data/docs/Cdss/GroundWater.html +945 -0
- data/docs/Cdss/Models/AdminCall.html +252 -0
- data/docs/Cdss/Models/Analysis.html +397 -0
- data/docs/Cdss/Models/CallAnalysis.html +140 -0
- data/docs/Cdss/Models/ClimateStation.html +249 -0
- data/docs/Cdss/Models/DiversionRecord.html +248 -0
- data/docs/Cdss/Models/Reading.html +301 -0
- data/docs/Cdss/Models/ReferenceTable.html +339 -0
- data/docs/Cdss/Models/RouteAnalysis.html +140 -0
- data/docs/Cdss/Models/SourceRoute.html +140 -0
- data/docs/Cdss/Models/Station.html +248 -0
- data/docs/Cdss/Models/Structure.html +259 -0
- data/docs/Cdss/Models/WaterClass.html +249 -0
- data/docs/Cdss/Models/WaterRight.html +255 -0
- data/docs/Cdss/Models/Well.html +251 -0
- data/docs/Cdss/Models.html +117 -0
- data/docs/Cdss/Parser.html +2155 -0
- data/docs/Cdss/Parsers/AdminCallsParser.html +201 -0
- data/docs/Cdss/Parsers/AnalysisParser.html +296 -0
- data/docs/Cdss/Parsers/BaseParser.html +207 -0
- data/docs/Cdss/Parsers/ClimateParser.html +253 -0
- data/docs/Cdss/Parsers/ReadingParser.html +201 -0
- data/docs/Cdss/Parsers/ReferenceTablesParser.html +201 -0
- data/docs/Cdss/Parsers/StationParser.html +201 -0
- data/docs/Cdss/Parsers/StructuresParser.html +305 -0
- data/docs/Cdss/Parsers/WaterRightsParser.html +219 -0
- data/docs/Cdss/Parsers/WellParser.html +357 -0
- data/docs/Cdss/Parsers.html +117 -0
- data/docs/Cdss/ReferenceTables.html +332 -0
- data/docs/Cdss/Structures.html +1132 -0
- data/docs/Cdss/SurfaceWater.html +798 -0
- data/docs/Cdss/Telemetry.html +763 -0
- data/docs/Cdss/Utils.html +1276 -0
- data/docs/Cdss/WaterRights.html +634 -0
- data/docs/Cdss.html +292 -0
- data/docs/_index.html +493 -0
- data/docs/class_list.html +54 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +58 -0
- data/docs/css/style.css +503 -0
- data/docs/file.README.html +108 -0
- data/docs/file_list.html +59 -0
- data/docs/frames.html +22 -0
- data/docs/index.html +108 -0
- data/docs/js/app.js +344 -0
- data/docs/js/full_list.js +242 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +790 -0
- data/docs/top-level-namespace.html +110 -0
- data/lib/cdss/admin_calls.rb +44 -0
- data/lib/cdss/analysis.rb +183 -0
- data/lib/cdss/client.rb +121 -0
- data/lib/cdss/climate.rb +155 -0
- data/lib/cdss/concerns/log_reading_attributes.rb +48 -0
- data/lib/cdss/concerns/well_reading_attributes.rb +56 -0
- data/lib/cdss/ground_water.rb +112 -0
- data/lib/cdss/models/admin_call.rb +45 -0
- data/lib/cdss/models/analysis.rb +77 -0
- data/lib/cdss/models/climate_station.rb +40 -0
- data/lib/cdss/models/reading.rb +54 -0
- data/lib/cdss/models/reference_table.rb +56 -0
- data/lib/cdss/models/station.rb +40 -0
- data/lib/cdss/models/structure.rb +101 -0
- data/lib/cdss/models/water_right.rb +47 -0
- data/lib/cdss/models/well.rb +43 -0
- data/lib/cdss/parser.rb +172 -0
- data/lib/cdss/parsers/admin_calls_parser.rb +47 -0
- data/lib/cdss/parsers/analysis_parser.rb +124 -0
- data/lib/cdss/parsers/base_parser.rb +18 -0
- data/lib/cdss/parsers/climate_parser.rb +86 -0
- data/lib/cdss/parsers/reading_parser.rb +90 -0
- data/lib/cdss/parsers/reference_tables_parser.rb +55 -0
- data/lib/cdss/parsers/station_parser.rb +42 -0
- data/lib/cdss/parsers/structures_parser.rb +96 -0
- data/lib/cdss/parsers/water_rights_parser.rb +77 -0
- data/lib/cdss/parsers/well_parser.rb +107 -0
- data/lib/cdss/reference_tables.rb +147 -0
- data/lib/cdss/structures.rb +235 -0
- data/lib/cdss/surface_water.rb +186 -0
- data/lib/cdss/telemetry.rb +98 -0
- data/lib/cdss/utils.rb +152 -0
- data/lib/cdss/version.rb +5 -0
- data/lib/cdss/water_rights.rb +95 -0
- data/lib/cdss.rb +27 -0
- data/sig/cdss/ruby.rbs +6 -0
- metadata +272 -0
@@ -0,0 +1,186 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cdss
|
4
|
+
# Provides methods for accessing surface water data from the CDSS API.
|
5
|
+
#
|
6
|
+
# This module includes functionality for retrieving surface water stations and their
|
7
|
+
# associated time series data at various time scales (daily, monthly, yearly).
|
8
|
+
module SurfaceWater
|
9
|
+
include Utils
|
10
|
+
|
11
|
+
# Fetches surface water stations based on various filtering criteria.
|
12
|
+
#
|
13
|
+
# @param [Hash, Array, nil] aoi Area of interest for spatial searches. If hash, must contain :latitude and :longitude keys.
|
14
|
+
# If array, must contain [longitude, latitude].
|
15
|
+
# @param [Integer, nil] radius Radius in miles for spatial search around aoi. Defaults to 20 if aoi is provided.
|
16
|
+
# @param [String, nil] abbrev Station abbreviation to filter by.
|
17
|
+
# @param [String, nil] county County name to filter stations.
|
18
|
+
# @param [Integer, nil] division Water division number to filter stations.
|
19
|
+
# @param [String, nil] station_name Name of the station to filter by.
|
20
|
+
# @param [String, nil] usgs_id USGS site ID to filter by.
|
21
|
+
# @param [Integer, nil] water_district Water district number to filter by.
|
22
|
+
# @param [String, nil] api_key Optional API key for authentication.
|
23
|
+
# @return [Array<Station>] Array of matching surface water station objects.
|
24
|
+
# @raise [ArgumentError] If aoi parameter is provided but invalid.
|
25
|
+
# @example Fetch stations in Denver county
|
26
|
+
# get_sw_stations(county: 'DENVER')
|
27
|
+
# @example Fetch stations within 10 miles of a point
|
28
|
+
# get_sw_stations(aoi: { latitude: 39.7392, longitude: -104.9903 }, radius: 10)
|
29
|
+
def get_sw_stations(aoi: nil, radius: nil, abbrev: nil, county: nil, division: nil, station_name: nil,
|
30
|
+
usgs_id: nil, water_district: nil, api_key: nil)
|
31
|
+
query = {
|
32
|
+
format: "json",
|
33
|
+
dateFormat: "spaceSepToSeconds",
|
34
|
+
abbrev: abbrev,
|
35
|
+
county: county,
|
36
|
+
division: division,
|
37
|
+
stationName: station_name,
|
38
|
+
usgsSiteId: usgs_id,
|
39
|
+
waterDistrict: water_district
|
40
|
+
}
|
41
|
+
|
42
|
+
if aoi
|
43
|
+
if aoi.is_a?(Hash) && aoi[:latitude] && aoi[:longitude]
|
44
|
+
query.merge!(longitude: aoi[:longitude], latitude: aoi[:latitude])
|
45
|
+
elsif aoi.is_a?(Array) && aoi.count == 2
|
46
|
+
query.merge!(longitude: aoi[0], latitude: aoi[1])
|
47
|
+
else
|
48
|
+
raise ArgumentError, "Invalid 'aoi' parameter"
|
49
|
+
end
|
50
|
+
query[:radius] = radius || 20
|
51
|
+
query[:units] = "miles"
|
52
|
+
end
|
53
|
+
|
54
|
+
fetch_paginated_data(
|
55
|
+
endpoint: "/surfacewater/surfacewaterstations/",
|
56
|
+
query: query
|
57
|
+
) { |data| Parser.parse_stations(data) }
|
58
|
+
end
|
59
|
+
|
60
|
+
# Fetches surface water time series data for specified stations.
|
61
|
+
#
|
62
|
+
# @param [String, nil] abbrev Station abbreviation.
|
63
|
+
# @param [String, nil] station_number Station number.
|
64
|
+
# @param [String, nil] usgs_id USGS site ID.
|
65
|
+
# @param [Date, nil] start_date Start date for time series data.
|
66
|
+
# @param [Date, nil] end_date End date for time series data.
|
67
|
+
# @param [String, nil] timescale Time interval for data aggregation. Valid values:
|
68
|
+
# - day, days, daily, d
|
69
|
+
# - month, months, monthly, mon, m
|
70
|
+
# - wyear, water_year, wyears, water_years, wateryear, wateryears, wy, year, years, yearly, annual, annually, yr, y
|
71
|
+
# @param [String, nil] api_key Optional API key for authentication.
|
72
|
+
# @return [Array<Reading>] Array of time series reading objects.
|
73
|
+
# @raise [ArgumentError] If an invalid timescale is provided.
|
74
|
+
# @example Fetch daily readings for a station
|
75
|
+
# get_sw_ts(abbrev: 'PLAKERCO', timescale: 'day', start_date: Date.new(2023, 1, 1))
|
76
|
+
def get_sw_ts(abbrev: nil, station_number: nil, usgs_id: nil, start_date: nil, end_date: nil, timescale: nil,
|
77
|
+
api_key: nil)
|
78
|
+
timescale ||= "day"
|
79
|
+
|
80
|
+
day_lst = %w[day days daily d]
|
81
|
+
month_lst = %w[month months monthly mon m]
|
82
|
+
year_lst = %w[wyear water_year wyears water_years wateryear wateryears wy year years
|
83
|
+
yearly annual annually yr y]
|
84
|
+
timescale_lst = day_lst + month_lst + year_lst
|
85
|
+
|
86
|
+
unless timescale_lst.include?(timescale)
|
87
|
+
valid_timescales = timescale_lst.join(", ")
|
88
|
+
raise ArgumentError, "Invalid 'timescale' argument: '#{timescale}'. Valid values are: #{valid_timescales}"
|
89
|
+
end
|
90
|
+
|
91
|
+
case timescale
|
92
|
+
when *day_lst
|
93
|
+
get_sw_ts_day(abbrev: abbrev, station_number: station_number, usgs_id: usgs_id, start_date: start_date,
|
94
|
+
end_date: end_date, api_key: api_key)
|
95
|
+
when *month_lst
|
96
|
+
get_sw_ts_month(abbrev: abbrev, station_number: station_number, usgs_id: usgs_id, start_date: start_date,
|
97
|
+
end_date: end_date, api_key: api_key)
|
98
|
+
when *year_lst
|
99
|
+
get_sw_ts_wyear(abbrev: abbrev, station_number: station_number, usgs_id: usgs_id, start_date: start_date,
|
100
|
+
end_date: end_date, api_key: api_key)
|
101
|
+
else
|
102
|
+
raise ArgumentError, "Invalid 'timescale' argument: '#{timescale}'"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
# Fetches daily surface water time series readings.
|
109
|
+
#
|
110
|
+
# @param [String, nil] abbrev Station abbreviation
|
111
|
+
# @param [String, nil] station_number Station number
|
112
|
+
# @param [String, nil] usgs_id USGS site ID
|
113
|
+
# @param [Date, nil] start_date Start date for readings
|
114
|
+
# @param [Date, nil] end_date End date for readings
|
115
|
+
# @param [String, nil] api_key Optional API key for authentication
|
116
|
+
# @return [Array<Reading>] Array of daily surface water readings
|
117
|
+
def get_sw_ts_day(abbrev: nil, station_number: nil, usgs_id: nil, start_date: nil, end_date: nil, api_key: nil)
|
118
|
+
query = {
|
119
|
+
format: "json",
|
120
|
+
dateFormat: "spaceSepToSeconds",
|
121
|
+
abbrev: abbrev,
|
122
|
+
stationNum: station_number,
|
123
|
+
usgsSiteId: usgs_id,
|
124
|
+
"min-measDate": start_date&.strftime("%m-%d-%Y"),
|
125
|
+
"max-measDate": end_date&.strftime("%m-%d-%Y")
|
126
|
+
}
|
127
|
+
|
128
|
+
fetch_paginated_data(
|
129
|
+
endpoint: "/surfacewater/surfacewatertsday/",
|
130
|
+
query: query
|
131
|
+
) { |data| Parser.parse_readings(data, timescale: :day) }
|
132
|
+
end
|
133
|
+
|
134
|
+
# Fetches monthly surface water time series readings.
|
135
|
+
#
|
136
|
+
# @param [String, nil] abbrev Station abbreviation
|
137
|
+
# @param [String, nil] station_number Station number
|
138
|
+
# @param [String, nil] usgs_id USGS site ID
|
139
|
+
# @param [Date, nil] start_date Start date for readings (year only)
|
140
|
+
# @param [Date, nil] end_date End date for readings (year only)
|
141
|
+
# @param [String, nil] api_key Optional API key for authentication
|
142
|
+
# @return [Array<Reading>] Array of monthly surface water readings
|
143
|
+
def get_sw_ts_month(abbrev: nil, station_number: nil, usgs_id: nil, start_date: nil, end_date: nil, api_key: nil)
|
144
|
+
query = {
|
145
|
+
format: "json",
|
146
|
+
dateFormat: "spaceSepToSeconds",
|
147
|
+
abbrev: abbrev,
|
148
|
+
stationNum: station_number,
|
149
|
+
usgsSiteId: usgs_id,
|
150
|
+
"min-calYear": start_date&.strftime("%Y"),
|
151
|
+
"max-calYear": end_date&.strftime("%Y")
|
152
|
+
}
|
153
|
+
|
154
|
+
fetch_paginated_data(
|
155
|
+
endpoint: "/surfacewater/surfacewatertsmonth/",
|
156
|
+
query: query
|
157
|
+
) { |data| Parser.parse_readings(data, timescale: :month) }
|
158
|
+
end
|
159
|
+
|
160
|
+
# Fetches water year time series readings.
|
161
|
+
#
|
162
|
+
# @param [String, nil] abbrev Station abbreviation
|
163
|
+
# @param [String, nil] station_number Station number
|
164
|
+
# @param [String, nil] usgs_id USGS site ID
|
165
|
+
# @param [Date, nil] start_date Start date for readings (year only)
|
166
|
+
# @param [Date, nil] end_date End date for readings (year only)
|
167
|
+
# @param [String, nil] api_key Optional API key for authentication
|
168
|
+
# @return [Array<Reading>] Array of water year readings
|
169
|
+
def get_sw_ts_wyear(abbrev: nil, station_number: nil, usgs_id: nil, start_date: nil, end_date: nil, api_key: nil)
|
170
|
+
query = {
|
171
|
+
format: "json",
|
172
|
+
dateFormat: "spaceSepToSeconds",
|
173
|
+
abbrev: abbrev,
|
174
|
+
stationNum: station_number,
|
175
|
+
usgsSiteId: usgs_id,
|
176
|
+
"min-waterYear": start_date&.strftime("%Y"),
|
177
|
+
"max-waterYear": end_date&.strftime("%Y")
|
178
|
+
}
|
179
|
+
|
180
|
+
fetch_paginated_data(
|
181
|
+
endpoint: "/surfacewater/surfacewatertswateryear/",
|
182
|
+
query: query
|
183
|
+
) { |data| Parser.parse_readings(data, timescale: :year) }
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cdss
|
4
|
+
# Provides methods for accessing telemetry station data from the CDSS API.
|
5
|
+
#
|
6
|
+
# This module includes functionality for retrieving telemetry stations and their
|
7
|
+
# associated time series data.
|
8
|
+
module Telemetry
|
9
|
+
include Utils
|
10
|
+
|
11
|
+
# Fetches telemetry stations based on various filtering criteria.
|
12
|
+
#
|
13
|
+
# @param [Hash, Array, nil] aoi Area of interest for spatial searches. If hash, must contain :latitude and :longitude keys.
|
14
|
+
# If array, must contain [longitude, latitude].
|
15
|
+
# @param [Integer, nil] radius Radius in miles for spatial search around aoi. Defaults to 20 if aoi is provided.
|
16
|
+
# @param [String, nil] abbrev Station abbreviation to filter by.
|
17
|
+
# @param [String, nil] county County name to filter stations.
|
18
|
+
# @param [Integer, nil] division Water division number to filter stations.
|
19
|
+
# @param [String, nil] gnis_id GNIS ID to filter by.
|
20
|
+
# @param [String, nil] usgs_id USGS station ID to filter by.
|
21
|
+
# @param [Integer, nil] water_district Water district number to filter by.
|
22
|
+
# @param [String, nil] wdid WDID to filter by.
|
23
|
+
# @return [Array<Station>] Array of matching telemetry station objects.
|
24
|
+
# @raise [ArgumentError] If aoi parameter is provided but invalid.
|
25
|
+
# @example Fetch stations in Denver county
|
26
|
+
# get_telemetry_stations(county: 'Denver')
|
27
|
+
# @example Fetch stations within 10 miles of a point
|
28
|
+
# get_telemetry_stations(aoi: { latitude: 39.7392, longitude: -104.9903 }, radius: 10)
|
29
|
+
def get_telemetry_stations(aoi: nil, radius: nil, abbrev: nil, county: nil, division: nil, gnis_id: nil,
|
30
|
+
usgs_id: nil, water_district: nil, wdid: nil)
|
31
|
+
query = {
|
32
|
+
format: "json",
|
33
|
+
dateFormat: "spaceSepToSeconds",
|
34
|
+
includeThirdParty: true,
|
35
|
+
abbrev: abbrev,
|
36
|
+
county: county,
|
37
|
+
division: division,
|
38
|
+
gnisId: gnis_id,
|
39
|
+
usgsStationId: usgs_id,
|
40
|
+
waterDistrict: water_district,
|
41
|
+
wdid: wdid
|
42
|
+
}
|
43
|
+
|
44
|
+
if aoi
|
45
|
+
if aoi.is_a?(Hash) && aoi[:latitude] && aoi[:longitude]
|
46
|
+
query.merge!(longitude: aoi[:longitude], latitude: aoi[:latitude])
|
47
|
+
elsif aoi.is_a?(Array) && aoi.count == 2
|
48
|
+
query.merge!(longitude: aoi[0], latitude: aoi[1])
|
49
|
+
else
|
50
|
+
raise ArgumentError, "Invalid 'aoi' parameter"
|
51
|
+
end
|
52
|
+
query[:radius] = radius || 20
|
53
|
+
query[:units] = "miles"
|
54
|
+
end
|
55
|
+
|
56
|
+
fetch_paginated_data(
|
57
|
+
endpoint: "/telemetrystations/telemetrystation/",
|
58
|
+
query: query
|
59
|
+
) { |data| Parser.parse_stations(data) }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Fetches telemetry time series data for specified stations.
|
63
|
+
#
|
64
|
+
# @param [String] abbrev Station abbreviation.
|
65
|
+
# @param [String, nil] parameter Telemetry parameter to retrieve. Defaults to 'DISCHRG'.
|
66
|
+
# @param [Date, nil] start_date Start date for time series data.
|
67
|
+
# @param [Date, nil] end_date End date for time series data.
|
68
|
+
# @param [String, nil] timescale Time interval for data. Valid values: 'day', 'hour', 'raw'.
|
69
|
+
# @param [Boolean] include_third_party Whether to include third-party data. Defaults to true.
|
70
|
+
# @return [Array<Reading>] Array of time series reading objects.
|
71
|
+
# @raise [ArgumentError] If an invalid timescale is provided.
|
72
|
+
# @example Fetch daily discharge data for a station
|
73
|
+
# get_telemetry_ts(abbrev: 'PLACHECO', parameter: 'DISCHRG', start_date: Date.new(2023, 1, 1))
|
74
|
+
def get_telemetry_ts(abbrev:, parameter: "DISCHRG", start_date: nil, end_date: nil, timescale: "day",
|
75
|
+
include_third_party: true)
|
76
|
+
timescales = %w[day hour raw]
|
77
|
+
unless timescales.include?(timescale)
|
78
|
+
raise ArgumentError, "Invalid 'timescale' argument: '#{timescale}'. Valid values are: #{timescales.join(', ')}"
|
79
|
+
end
|
80
|
+
|
81
|
+
query = {
|
82
|
+
format: "json",
|
83
|
+
dateFormat: "spaceSepToSeconds",
|
84
|
+
abbrev: abbrev,
|
85
|
+
parameter: parameter,
|
86
|
+
includeThirdParty: include_third_party.to_s
|
87
|
+
}
|
88
|
+
|
89
|
+
query[:startDate] = start_date&.strftime("%m-%d-%Y") if start_date
|
90
|
+
query[:endDate] = end_date&.strftime("%m-%d-%Y") if end_date
|
91
|
+
|
92
|
+
fetch_paginated_data(
|
93
|
+
endpoint: "/telemetrystations/telemetrytimeseries#{timescale}/",
|
94
|
+
query: query
|
95
|
+
) { |data| Parser.parse_readings(data, timescale: timescale.to_sym) }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/lib/cdss/utils.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cdss
|
4
|
+
# Provides utility methods for handling dates, query parameters, and API pagination.
|
5
|
+
#
|
6
|
+
# This module contains helper methods used across the CDSS API client for
|
7
|
+
# data formatting, safe type conversion, and managing paginated responses.
|
8
|
+
module Utils
|
9
|
+
# Splits a date range into yearly chunks.
|
10
|
+
#
|
11
|
+
# @param [Date, nil] start_date Beginning of the date range
|
12
|
+
# @param [Date, nil] end_date End of the date range
|
13
|
+
# @return [Array<Array<Date>>] Array of date pairs representing yearly chunks
|
14
|
+
# @example Split a multi-year range
|
15
|
+
# batch_dates(Date.new(2020,1,1), Date.new(2022,6,30))
|
16
|
+
# #=> [[2020-01-01, 2020-12-31], [2021-01-01, 2021-12-31], [2022-01-01, 2022-06-30]]
|
17
|
+
def batch_dates(start_date, end_date)
|
18
|
+
start_date ||= Date.new(1900, 1, 1)
|
19
|
+
end_date ||= Date.today
|
20
|
+
start_year = start_date.year
|
21
|
+
end_year = end_date.year
|
22
|
+
if start_year == end_year
|
23
|
+
[[start_date, end_date]]
|
24
|
+
else
|
25
|
+
dates = []
|
26
|
+
# First year
|
27
|
+
dates << [start_date, Date.new(start_year, 12, 31)]
|
28
|
+
# Middle years
|
29
|
+
((start_year + 1)...end_year).each do |year|
|
30
|
+
dates << [Date.new(year, 1, 1), Date.new(year, 12, 31)]
|
31
|
+
end
|
32
|
+
# Last year
|
33
|
+
dates << [Date.new(end_year, 1, 1), end_date]
|
34
|
+
dates
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Formats a query parameter value for API requests.
|
39
|
+
#
|
40
|
+
# @param [Object] value The value to format
|
41
|
+
# @return [String, nil] Formatted value or nil if input is nil
|
42
|
+
# @example Format an array parameter
|
43
|
+
# format_query_param(['a', 'b']) #=> "a,b"
|
44
|
+
def format_query_param(value)
|
45
|
+
return nil if value.nil?
|
46
|
+
return value.join(",") if value.is_a?(Array)
|
47
|
+
|
48
|
+
value.to_s
|
49
|
+
end
|
50
|
+
|
51
|
+
# Builds a query hash for API requests.
|
52
|
+
#
|
53
|
+
# @param [Hash] params Query parameters to include
|
54
|
+
# @param [Boolean] encode Whether to URL encode parameter values
|
55
|
+
# @return [Hash] Query hash with formatted parameters
|
56
|
+
# @example Build a simple query
|
57
|
+
# build_query({ name: "test", id: 123 })
|
58
|
+
# #=> { format: "json", name: "test", id: "123" }
|
59
|
+
def build_query(params = {}, encode: false)
|
60
|
+
base_query = {
|
61
|
+
format: "json"
|
62
|
+
}
|
63
|
+
params.each do |key, value|
|
64
|
+
formatted_value = format_query_param(value)
|
65
|
+
if formatted_value
|
66
|
+
base_query[key] = encode ? URI.encode_www_form_component(formatted_value) : formatted_value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
base_query
|
70
|
+
end
|
71
|
+
|
72
|
+
# Formats a date for API requests.
|
73
|
+
#
|
74
|
+
# @param [Date] date The date to format
|
75
|
+
# @param [Boolean] special_format Whether to use forward slashes instead of hyphens
|
76
|
+
# @return [String, nil] Formatted date string or nil if input is nil
|
77
|
+
# @example Format a date
|
78
|
+
# format_date(Date.new(2023,1,1)) #=> "01-01-2023"
|
79
|
+
def format_date(date, special_format: false)
|
80
|
+
return nil unless date
|
81
|
+
|
82
|
+
date = date.strftime("%m-%d-%Y")
|
83
|
+
special_format ? date.gsub("-", "%2F") : date
|
84
|
+
end
|
85
|
+
|
86
|
+
# Safely parses a timestamp string.
|
87
|
+
#
|
88
|
+
# @param [String] datetime_str Timestamp string to parse
|
89
|
+
# @return [DateTime, nil] Parsed DateTime object or nil if parsing fails
|
90
|
+
# @example Parse a timestamp
|
91
|
+
# parse_timestamp("2023-01-01 12:00:00")
|
92
|
+
def parse_timestamp(datetime_str)
|
93
|
+
DateTime.parse(datetime_str) if datetime_str
|
94
|
+
rescue ArgumentError
|
95
|
+
nil
|
96
|
+
end
|
97
|
+
|
98
|
+
# Safely converts a value to a float.
|
99
|
+
#
|
100
|
+
# @param [Object] value Value to convert
|
101
|
+
# @return [Float, nil] Converted float or nil if conversion fails
|
102
|
+
# @example Convert a string to float
|
103
|
+
# safe_float("123.45") #=> 123.45
|
104
|
+
def safe_float(value)
|
105
|
+
Float(value)
|
106
|
+
rescue TypeError, ArgumentError
|
107
|
+
nil
|
108
|
+
end
|
109
|
+
|
110
|
+
# Safely converts a value to an integer.
|
111
|
+
#
|
112
|
+
# @param [Object] value Value to convert
|
113
|
+
# @return [Integer, nil] Converted integer or nil if conversion fails
|
114
|
+
# @example Convert a string to integer
|
115
|
+
# safe_integer("123") #=> 123
|
116
|
+
def safe_integer(value)
|
117
|
+
Integer(value)
|
118
|
+
rescue TypeError, ArgumentError
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
|
122
|
+
# Fetches all pages of data from a paginated API endpoint.
|
123
|
+
#
|
124
|
+
# @param [String] endpoint API endpoint path
|
125
|
+
# @param [Hash] query Query parameters for the request
|
126
|
+
# @yield [Hash] Block to process each page of response data
|
127
|
+
# @yieldreturn [Array] Processed records from the response
|
128
|
+
# @return [Array] Combined results from all pages
|
129
|
+
# @example Fetch paginated data
|
130
|
+
# fetch_paginated_data(endpoint: "/api/data", query: { type: "test" }) do |data|
|
131
|
+
# process_data(data)
|
132
|
+
# end
|
133
|
+
def fetch_paginated_data(endpoint:, query:)
|
134
|
+
page_size = 50_000
|
135
|
+
page_index = 1
|
136
|
+
results = []
|
137
|
+
loop do
|
138
|
+
query = query.merge(pageSize: page_size, pageIndex: page_index)
|
139
|
+
response = get(endpoint, query: query)
|
140
|
+
data = handle_response(response)
|
141
|
+
records = yield(data)
|
142
|
+
break if records.empty?
|
143
|
+
|
144
|
+
results.concat(records)
|
145
|
+
break if records.size < page_size
|
146
|
+
|
147
|
+
page_index += 1
|
148
|
+
end
|
149
|
+
results
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
data/lib/cdss/version.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cdss
|
4
|
+
# Provides methods for accessing water rights data from the CDSS API.
|
5
|
+
#
|
6
|
+
# This module includes functionality for retrieving water rights net amounts
|
7
|
+
# and transactions data based on various spatial and attribute-based searches.
|
8
|
+
module WaterRights
|
9
|
+
include Utils
|
10
|
+
|
11
|
+
# Fetches water rights net amounts data based on various filtering criteria.
|
12
|
+
#
|
13
|
+
# @param [Hash, Array, nil] aoi Area of interest for spatial searches. If hash, must contain :latitude and :longitude keys.
|
14
|
+
# If array, must contain [longitude, latitude].
|
15
|
+
# @param [Integer, nil] radius Radius in miles for spatial search around aoi. Defaults to 20 if aoi is provided.
|
16
|
+
# @param [String, nil] county County name to filter rights.
|
17
|
+
# @param [Integer, nil] division Water division number to filter rights.
|
18
|
+
# @param [Integer, nil] water_district Water district number to filter rights.
|
19
|
+
# @param [String, nil] wdid WDID code of water right.
|
20
|
+
# @return [Array<WaterRight>] Array of water right objects with net amounts.
|
21
|
+
# @raise [ArgumentError] If aoi parameter is provided but invalid.
|
22
|
+
def get_water_rights_net_amounts(aoi: nil, radius: nil, county: nil, division: nil, water_district: nil, wdid: nil)
|
23
|
+
query = {
|
24
|
+
format: "json",
|
25
|
+
dateFormat: "spaceSepToSeconds",
|
26
|
+
units: "miles",
|
27
|
+
county: county,
|
28
|
+
division: division,
|
29
|
+
waterDistrict: water_district,
|
30
|
+
wdid: wdid
|
31
|
+
}
|
32
|
+
query.merge!(process_aoi(aoi, radius)) if aoi
|
33
|
+
fetch_paginated_data(
|
34
|
+
endpoint: "/waterrights/netamount/",
|
35
|
+
query: query
|
36
|
+
) { |data| Parser.parse_water_rights(data, type: :net_amount) }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Fetches water rights transactions data based on various filtering criteria.
|
40
|
+
#
|
41
|
+
# @param [Hash, Array, nil] aoi Area of interest for spatial searches. If hash, must contain :latitude and :longitude keys.
|
42
|
+
# If array, must contain [longitude, latitude].
|
43
|
+
# @param [Integer, nil] radius Radius in miles for spatial search around aoi. Defaults to 20 if aoi is provided.
|
44
|
+
# @param [String, nil] county County name to filter transactions.
|
45
|
+
# @param [Integer, nil] division Water division number to filter transactions.
|
46
|
+
# @param [Integer, nil] water_district Water district number to filter transactions.
|
47
|
+
# @param [String, nil] wdid WDID code of water right.
|
48
|
+
# @return [Array<WaterRight>] Array of water right objects with transactions.
|
49
|
+
# @raise [ArgumentError] If aoi parameter is provided but invalid.
|
50
|
+
def get_water_rights_transactions(aoi: nil, radius: nil, county: nil, division: nil, water_district: nil, wdid: nil)
|
51
|
+
query = {
|
52
|
+
format: "json",
|
53
|
+
dateFormat: "spaceSepToSeconds",
|
54
|
+
units: "miles",
|
55
|
+
county: county,
|
56
|
+
division: division,
|
57
|
+
waterDistrict: water_district,
|
58
|
+
wdid: wdid
|
59
|
+
}
|
60
|
+
query.merge!(process_aoi(aoi, radius)) if aoi
|
61
|
+
fetch_paginated_data(
|
62
|
+
endpoint: "/waterrights/transaction/",
|
63
|
+
query: query
|
64
|
+
) { |data| Parser.parse_water_rights(data, type: :transaction) }
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# Processes the area of interest (AOI) parameter for spatial searches.
|
70
|
+
#
|
71
|
+
# @param [Hash, Array] aoi Area of interest with location data
|
72
|
+
# @param [Integer, nil] radius Search radius in miles
|
73
|
+
# @return [Hash] Processed location and radius query parameters
|
74
|
+
# @raise [ArgumentError] If AOI parameter is invalid
|
75
|
+
# @example Process a hash-based AOI
|
76
|
+
# process_aoi({ latitude: 39.7392, longitude: -104.9903 }, 20)
|
77
|
+
def process_aoi(aoi, radius)
|
78
|
+
if aoi.is_a?(Hash) && aoi[:latitude] && aoi[:longitude]
|
79
|
+
{
|
80
|
+
longitude: aoi[:longitude],
|
81
|
+
latitude: aoi[:latitude],
|
82
|
+
radius: radius || 20
|
83
|
+
}
|
84
|
+
elsif aoi.is_a?(Array) && aoi.count == 2
|
85
|
+
{
|
86
|
+
longitude: aoi[0],
|
87
|
+
latitude: aoi[1],
|
88
|
+
radius: radius || 20
|
89
|
+
}
|
90
|
+
else
|
91
|
+
raise ArgumentError, "Invalid 'aoi' parameter"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/cdss.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "zeitwerk"
|
4
|
+
require "dry-configurable"
|
5
|
+
require "httparty"
|
6
|
+
require "json"
|
7
|
+
|
8
|
+
module Cdss
|
9
|
+
@loader = Zeitwerk::Loader.for_gem
|
10
|
+
@loader.enable_reloading
|
11
|
+
@loader.setup
|
12
|
+
|
13
|
+
extend Dry::Configurable
|
14
|
+
setting :user_agent, default: -> { "Cdss Ruby Gem/#{VERSION}" }
|
15
|
+
setting :timeout, default: 30
|
16
|
+
setting :base_url, default: "https://dwr.state.co.us/Rest/GET/api/v2"
|
17
|
+
setting :default_parameter, default: "DISCHRG"
|
18
|
+
setting :debug, default: false
|
19
|
+
|
20
|
+
class << self
|
21
|
+
attr_reader :loader
|
22
|
+
|
23
|
+
def client(**options)
|
24
|
+
Cdss::Client.new(**options)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|