1use actix_http::StatusCode;
4use postgis_diesel::types::{Point, Polygon};
5use uuid::Uuid;
6
7use crate::{
8 config::data::SharedPool,
9 error::ServiceError,
10 model::{
11 dto::{
12 base_layer_images::BaseLayerImageDto, layers::LayerDto, MapDto, MapSearchParameters,
13 NewMapDto, Page, PageParameters, UpdateMapDto, UpdateMapGeometryDto,
14 },
15 entity::{base_layer_images::BaseLayerImages, layers::Layer, Map},
16 r#enum::layer_type::LayerType,
17 },
18};
19
20const LAYER_TYPES: [LayerType; 6] = [
22 LayerType::Base,
23 LayerType::Drawing,
24 LayerType::Soiltexture,
25 LayerType::Hydrology,
26 LayerType::Shade,
27 LayerType::Plants,
28];
29
30pub async fn find(
35 search_parameters: MapSearchParameters,
36 page_parameters: PageParameters,
37 pool: &SharedPool,
38) -> Result<Page<MapDto>, ServiceError> {
39 let mut conn = pool.get().await?;
40 let result = Map::find(search_parameters, page_parameters, &mut conn).await?;
41 Ok(result)
42}
43
44pub async fn find_by_id(id: i32, pool: &SharedPool) -> Result<MapDto, ServiceError> {
49 let mut conn = pool.get().await?;
50 let result = Map::find_by_id(id, &mut conn).await?;
51 Ok(result)
52}
53
54pub async fn create(
59 new_map: NewMapDto,
60 user_id: Uuid,
61 pool: &SharedPool,
62) -> Result<MapDto, ServiceError> {
63 let mut conn = pool.get().await?;
64
65 if Map::is_name_taken(&new_map.name, &mut conn).await? {
66 return Err(ServiceError::new(
67 StatusCode::CONFLICT,
68 "Map name already taken",
69 ));
70 }
71
72 let geometry_validation_result = is_valid_map_geometry(&new_map.geometry);
73 if let Some(error) = geometry_validation_result {
74 return Err(error);
75 }
76
77 let result = Map::create(new_map, user_id, &mut conn).await?;
78 for (layer_type, order_index) in LAYER_TYPES.iter().zip(0..) {
79 let new_layer = LayerDto {
80 id: Uuid::new_v4(),
81 type_: *layer_type,
82 name: format!("{layer_type} Layer"),
83 is_alternative: false,
84 map_id: result.id,
85 order_index,
86 marked_deleted: false,
87 };
88 let layer = Layer::create(result.id, new_layer, &mut conn).await?;
89
90 if layer.type_ == LayerType::Base {
94 BaseLayerImages::create(
95 BaseLayerImageDto {
96 id: Uuid::new_v4(),
97 layer_id: layer.id,
98 path: String::new(),
99 rotation: 0.0,
100 scale: 100.0,
101 x: 0,
102 y: 0,
103 },
104 &mut conn,
105 )
106 .await?;
107 }
108 }
109
110 Ok(result)
111}
112
113pub async fn update(
120 map_update: UpdateMapDto,
121 id: i32,
122 user_id: Uuid,
123 pool: &SharedPool,
124) -> Result<MapDto, ServiceError> {
125 let mut conn = pool.get().await?;
126 let map = Map::find_by_id(id, &mut conn).await?;
127 if map.created_by != user_id {
128 return Err(ServiceError {
129 status_code: StatusCode::FORBIDDEN,
130 reason: "no permission to update data".to_owned(),
131 });
132 }
133 let result = Map::update(map_update, id, &mut conn).await?;
134 Ok(result)
135}
136
137pub async fn update_geometry(
144 map_update_geometry: UpdateMapGeometryDto,
145 id: i32,
146 user_id: Uuid,
147 pool: &SharedPool,
148) -> Result<MapDto, ServiceError> {
149 let mut conn = pool.get().await?;
150 let map = Map::find_by_id(id, &mut conn).await?;
151 if map.created_by != user_id {
152 return Err(ServiceError {
153 status_code: StatusCode::FORBIDDEN,
154 reason: "no permission to update geometry".to_owned(),
155 });
156 }
157
158 let geometry_validation_result = is_valid_map_geometry(&map_update_geometry.geometry);
159 if let Some(error) = geometry_validation_result {
160 return Err(error);
161 }
162
163 let result = Map::update_geometry(map_update_geometry, id, &mut conn).await?;
164 Ok(result)
165}
166
167pub async fn delete_by_id(
174 id: i32,
175 user_id: Uuid,
176 pool: &SharedPool,
177) -> Result<MapDto, ServiceError> {
178 let mut conn = pool.get().await?;
179
180 let map = Map::find_by_id(id, &mut conn).await?;
181 if map.created_by != user_id {
182 return Err(ServiceError {
183 status_code: StatusCode::FORBIDDEN,
184 reason: "no permission to delete map".to_owned(),
185 });
186 }
187
188 let result = Map::mark_for_deletion(id, &mut conn).await?;
189
190 Ok(result)
191}
192
193fn is_valid_map_geometry(geometry: &Polygon<Point>) -> Option<ServiceError> {
195 if geometry.rings.len() != 1 {
196 return Some(ServiceError {
197 status_code: StatusCode::BAD_REQUEST,
198 reason: "Map geometry must have exactly one ring".to_owned(),
199 });
200 }
201
202 let geometry_points_length = geometry.rings.get(0).unwrap_or(&Vec::new()).len();
203
204 if geometry_points_length < 3 + 1 {
205 return Some(ServiceError {
206 status_code: StatusCode::BAD_REQUEST,
207 reason: "Map geometry must be a polygon of at least three points.".to_owned(),
208 });
209 }
210
211 None
212}