use crate::{ArcGISClient, ErrorKind, FeatureSet, GPExecuteResult, Result};
use tracing::instrument;
use super::types::{
ProfileParameters, ProfileResult, SummarizeElevationParameters, SummarizeElevationResult,
ViewshedParameters, ViewshedResult,
};
#[derive(Clone)]
pub struct ElevationClient<'a> {
url: String,
client: &'a ArcGISClient,
}
impl<'a> ElevationClient<'a> {
pub fn new(client: &'a ArcGISClient) -> Self {
ElevationClient {
url: "https://elevation.arcgis.com/arcgis/rest/services/Tools/ElevationSync/GPServer"
.to_string(),
client,
}
}
pub fn with_url(url: impl Into<String>, client: &'a ArcGISClient) -> Self {
ElevationClient {
url: url.into(),
client,
}
}
#[instrument(skip(self, params))]
pub async fn profile(&self, params: ProfileParameters) -> Result<ProfileResult> {
tracing::debug!("Generating elevation profile");
let profile_url = format!("{}/Profile/execute", self.url);
let mut request = self
.client
.http()
.get(&profile_url)
.query(&[("f", "json")])
.query(¶ms);
if let Some(token) = self.client.get_token_if_required().await? {
request = request.query(&[("token", token)]);
}
tracing::debug!(url = %profile_url, "Sending profile request");
let response = request.send().await?;
let response_body = response.text().await?;
tracing::debug!(
response_length = response_body.len(),
response_body = %response_body,
"Received profile response"
);
let gp_result: GPExecuteResult = serde_json::from_str(&response_body)?;
tracing::debug!(
result_count = gp_result.results().len(),
message_count = gp_result.messages().len(),
"Parsed GP execute result"
);
let output_param = gp_result.results().first().ok_or_else(|| {
tracing::error!("GP result missing results array");
crate::Error::from(ErrorKind::Api {
code: 0,
message: "Elevation profile result missing results array".to_string(),
})
})?;
tracing::debug!(
param_name = ?output_param.param_name(),
data_type = ?output_param.data_type(),
"Extracting profile parameter"
);
let feature_set_value = output_param.value().as_ref().ok_or_else(|| {
tracing::error!("OutputProfile parameter missing value");
crate::Error::from(ErrorKind::Api {
code: 0,
message: "Elevation profile parameter missing value field".to_string(),
})
})?;
let feature_set: FeatureSet = serde_json::from_value(feature_set_value.clone())?;
tracing::debug!(
feature_count = feature_set.features().len(),
geometry_type = ?feature_set.geometry_type(),
"Extracted profile FeatureSet"
);
let result = ProfileResult::new(feature_set);
tracing::debug!("Profile generated");
Ok(result)
}
#[instrument(skip(self, params))]
pub async fn summarize_elevation(
&self,
params: SummarizeElevationParameters,
) -> Result<SummarizeElevationResult> {
tracing::debug!("Summarizing elevation");
let summarize_url = format!("{}/SummarizeElevation/execute", self.url);
let mut request = self
.client
.http()
.get(&summarize_url)
.query(&[("f", "json")])
.query(¶ms);
if let Some(token) = self.client.get_token_if_required().await? {
request = request.query(&[("token", token)]);
}
tracing::debug!(url = %summarize_url, "Sending summarize elevation request");
let response = request.send().await?;
let response_body = response.text().await?;
tracing::debug!(
response_length = response_body.len(),
response_body = %response_body,
"Received summarize elevation response"
);
let result: SummarizeElevationResult = serde_json::from_str(&response_body)?;
tracing::debug!(
min = ?result.min_elevation(),
max = ?result.max_elevation(),
mean = ?result.mean_elevation(),
"Elevation summarized"
);
Ok(result)
}
#[instrument(skip(self, params))]
pub async fn viewshed(&self, params: ViewshedParameters) -> Result<ViewshedResult> {
tracing::debug!(
max_distance = ?params.maximum_distance(),
observer_height = ?params.observer_height(),
"Computing viewshed"
);
let viewshed_url = format!("{}/Viewshed/execute", self.url);
let mut request = self
.client
.http()
.get(&viewshed_url)
.query(&[("f", "json")])
.query(¶ms);
if let Some(token) = self.client.get_token_if_required().await? {
request = request.query(&[("token", token)]);
}
tracing::debug!(url = %viewshed_url, "Sending viewshed request");
let response = request.send().await?;
let response_body = response.text().await?;
tracing::debug!(
response_length = response_body.len(),
response_body = %response_body,
"Received viewshed response"
);
let result: ViewshedResult = serde_json::from_str(&response_body)?;
tracing::debug!(
visible_area = ?result.visible_area(),
percent = ?result.percent_visible(),
"Viewshed computed"
);
Ok(result)
}
}