bbox_tile_server/datasource/
wms_fcgi.rs1use crate::config::WmsFcgiSourceParamsCfg;
4use crate::datasource::{LayerInfo, SourceType, TileSource, TileSourceError};
5use crate::filter_params::FilterParams;
6use crate::service::{QueryExtent, TmsExtensions};
7use async_trait::async_trait;
8use bbox_core::service::OgcApiService;
9use bbox_core::{Format, TileResponse};
10use bbox_map_server::endpoints::wms_fcgi_req;
11pub use bbox_map_server::{
12 endpoints::FcgiError, endpoints::HttpRequestParams, metrics::WmsMetrics, MapService,
13};
14use once_cell::sync::OnceCell;
15use std::num::NonZeroU16;
16use tile_grid::{Tms, Xyz};
17use tilejson::{tilejson, TileJSON};
18
19#[derive(Clone)]
20pub struct WmsFcgiSource {
21 map_service: Option<MapService>,
23 pub project: String,
24 pub suffix: String,
25 pub query: String,
26 pub tile_size: Option<NonZeroU16>,
27}
28
29impl WmsFcgiSource {
30 pub fn from_config(cfg: &WmsFcgiSourceParamsCfg) -> Self {
31 let project = cfg.project.clone();
32 let suffix = cfg.suffix.clone();
33 let query = format!(
34 "map={project}.{suffix}&SERVICE=WMS&REQUEST=GetMap&VERSION=1.3&LAYERS={}&STYLES=&{}",
35 cfg.layers,
36 cfg.params.as_ref().unwrap_or(&"".to_string()),
37 );
38 WmsFcgiSource {
39 map_service: None,
40 project,
41 suffix,
42 query,
43 tile_size: cfg.tile_size,
44 }
45 }
46
47 pub fn get_map_request(&self, extent_info: &QueryExtent, format: &Format) -> String {
48 let (width, height) = if let Some(size) = self.tile_size {
49 (size, size)
50 } else {
51 (extent_info.tile_width, extent_info.tile_height)
52 };
53 let extent = &extent_info.extent;
54 format!(
55 "{}&CRS=EPSG:{}&BBOX={},{},{},{}&WIDTH={width}&HEIGHT={height}&FORMAT={}",
56 self.query,
57 extent_info.srid,
58 extent.left,
59 extent.bottom,
60 extent.right,
61 extent.top,
62 format.content_type()
63 )
64 }
65
66 async fn bbox_request(
67 &self,
68 extent_info: &QueryExtent,
69 format: &Format,
70 request_params: HttpRequestParams<'_>,
71 ) -> Result<TileResponse, TileSourceError> {
72 let fcgi_dispatcher = self
73 .map_service
74 .as_ref()
75 .expect("not initialized")
76 .fcgi_dispatcher(&self.suffix)
77 .ok_or(TileSourceError::SuffixNotFound(self.suffix.clone()))?;
78 let fcgi_query = self.get_map_request(extent_info, format);
79 let project = &self.project;
80 let body = "".to_string();
81 wms_fcgi_req(
82 fcgi_dispatcher,
83 &fcgi_query,
84 request_params,
85 "GET",
86 body,
87 project,
88 )
89 .await
90 .map_err(Into::into)
91 }
92}
93
94#[async_trait]
95impl TileSource for WmsFcgiSource {
96 async fn xyz_request(
97 &self,
98 tms: &Tms,
99 tile: &Xyz,
100 _filter: &FilterParams,
101 format: &Format,
102 request_params: HttpRequestParams<'_>,
103 ) -> Result<TileResponse, TileSourceError> {
104 let extent_info = tms.xyz_extent(tile)?;
105 self.bbox_request(&extent_info, format, request_params)
106 .await
107 }
108 fn source_type(&self) -> SourceType {
109 SourceType::Raster
110 }
111 fn set_map_service(&mut self, service: &MapService) {
112 self.map_service = Some(service.clone());
113 }
114 fn wms_metrics(&self) -> &'static WmsMetrics {
115 static DUMMY_METRICS: OnceCell<WmsMetrics> = OnceCell::new();
116 if let Some(map_service) = &self.map_service {
117 map_service.metrics()
118 } else {
119 DUMMY_METRICS.get_or_init(WmsMetrics::default)
120 }
121 }
122 async fn tilejson(&self, _tms: &Tms, format: &Format) -> Result<TileJSON, TileSourceError> {
123 let mut tj = tilejson! { tiles: vec![] };
124 tj.other
125 .insert("format".to_string(), format.file_suffix().into());
126 Ok(tj)
127 }
128 async fn layers(&self) -> Result<Vec<LayerInfo>, TileSourceError> {
129 Ok(vec![LayerInfo {
130 name: self.project.clone(), geometry_type: None,
132 style: None,
133 }])
134 }
135}