mirror of
https://github.com/CIMEngine/cimengine-build-tools.git
synced 2024-11-21 19:56:22 +03:00
refactor: traits
This commit is contained in:
parent
1ab51d33d3
commit
2da4604a67
2 changed files with 159 additions and 102 deletions
152
src/types.rs
152
src/types.rs
|
@ -1,5 +1,6 @@
|
|||
use clap::{Parser, Subcommand};
|
||||
use geo::{BoundingRect, Geometry, MultiPolygon, Point, Polygon};
|
||||
use geo::{Geometry, MultiPolygon};
|
||||
use geo::{Point, Polygon};
|
||||
use geojson::{Feature, FeatureCollection, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
@ -127,8 +128,8 @@ pub struct Marker {
|
|||
pub ty: MarkerType,
|
||||
}
|
||||
|
||||
impl Marker {
|
||||
pub fn to_feature(&self) -> geojson::Feature {
|
||||
impl ToFeature for Marker {
|
||||
fn to_feature(&self) -> geojson::Feature {
|
||||
geojson::Feature {
|
||||
geometry: Some(geojson::Geometry::new(Value::Point(vec![
|
||||
self.coordinates.x(),
|
||||
|
@ -171,25 +172,146 @@ pub enum Territory {
|
|||
MultiPolygon(MultiPolygon),
|
||||
}
|
||||
|
||||
impl Territory {
|
||||
pub fn to_feature(&self) -> geojson::Feature {
|
||||
match self {
|
||||
Territory::Polygon(p) => geojson::Feature {
|
||||
geometry: Some(geojson::Geometry::new(p.into())),
|
||||
properties: None,
|
||||
impl ToTerritory for MultiPolygon {
|
||||
fn to_territory(&self) -> Territory {
|
||||
Territory::MultiPolygon(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCountryFeature for MultiPolygon {
|
||||
fn to_country_feature(&self, id: &String, config: &CountryConfig) -> geojson::Feature {
|
||||
geojson::Feature {
|
||||
geometry: Some(geojson::Geometry::new((self).into())),
|
||||
properties: Some(
|
||||
serde_json::Map::from_iter([
|
||||
("id".to_owned(), json!(id)),
|
||||
("type".to_owned(), json!("country")),
|
||||
("fill".to_owned(), json!(config.fill)),
|
||||
("stroke".to_owned(), json!(config.stroke)),
|
||||
("tags".to_owned(), json!(config.tags)),
|
||||
])
|
||||
.into(),
|
||||
),
|
||||
|
||||
bbox: None,
|
||||
id: None,
|
||||
foreign_members: None,
|
||||
},
|
||||
Territory::MultiPolygon(mp) => geojson::Feature {
|
||||
geometry: Some(geojson::Geometry::new(mp.into())),
|
||||
properties: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToFeatures for Vec<Marker> {
|
||||
fn to_features(&self) -> Vec<geojson::Feature> {
|
||||
self.iter().map(|m| m.to_feature()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCollection for Vec<geojson::Feature> {
|
||||
fn to_collection(self) -> geojson::FeatureCollection {
|
||||
geojson::FeatureCollection {
|
||||
features: self,
|
||||
bbox: None,
|
||||
id: None,
|
||||
foreign_members: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSplitGeo for FeatureCollection {
|
||||
fn split_geo(&self) -> (Vec<Marker>, Vec<Territory>) {
|
||||
let mut markers: Vec<Marker> = vec![];
|
||||
let mut territories: Vec<Territory> = vec![];
|
||||
|
||||
self.features.iter().for_each(|f| {
|
||||
let properties = f.properties.clone().unwrap();
|
||||
|
||||
let geometry: Geometry = f.geometry.clone().unwrap().try_into().unwrap();
|
||||
|
||||
match geometry {
|
||||
Geometry::Point(p) => {
|
||||
let ty = match properties
|
||||
.get("type")
|
||||
.expect("Missing marker type")
|
||||
.to_string()
|
||||
.trim_matches('"')
|
||||
{
|
||||
"capital" | "capital-city" => MarkerType::Capital,
|
||||
"city" => MarkerType::City,
|
||||
"landmark" => MarkerType::Landmark,
|
||||
|
||||
t => panic!("Invalid marker type: {}", t),
|
||||
};
|
||||
|
||||
markers.push(Marker {
|
||||
coordinates: p,
|
||||
title: properties
|
||||
.get("title")
|
||||
.expect("Missing marker title")
|
||||
.to_string(),
|
||||
description: properties
|
||||
.get("description")
|
||||
.unwrap_or(&json!(""))
|
||||
.to_string(),
|
||||
ty,
|
||||
})
|
||||
}
|
||||
|
||||
Geometry::MultiPolygon(mp) => territories.push(Territory::MultiPolygon(mp)),
|
||||
|
||||
Geometry::Polygon(p) => territories.push(Territory::Polygon(p)),
|
||||
|
||||
_ => panic!("Unexpected geometry type"),
|
||||
}
|
||||
});
|
||||
|
||||
(markers, territories)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnsplitGeo for (Vec<Marker>, Feature) {
|
||||
fn unsplit_geo(self) -> FeatureCollection {
|
||||
let (markers, territories) = self;
|
||||
|
||||
let mut features: Vec<geojson::Feature> = markers.to_features();
|
||||
features.push(territories);
|
||||
|
||||
features.to_collection()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToMultiPolygon for Polygon {
|
||||
fn to_mp(&self) -> MultiPolygon {
|
||||
MultiPolygon::new(vec![self.clone()])
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToFeature {
|
||||
fn to_feature(&self) -> geojson::Feature;
|
||||
}
|
||||
|
||||
pub trait ToFeatures {
|
||||
fn to_features(&self) -> Vec<geojson::Feature>;
|
||||
}
|
||||
|
||||
pub trait ToCountryFeature {
|
||||
fn to_country_feature(&self, id: &String, config: &CountryConfig) -> geojson::Feature;
|
||||
}
|
||||
|
||||
pub trait ToCollection {
|
||||
fn to_collection(self) -> geojson::FeatureCollection;
|
||||
}
|
||||
|
||||
pub trait ToSplitGeo {
|
||||
fn split_geo(&self) -> (Vec<Marker>, Vec<Territory>);
|
||||
}
|
||||
|
||||
pub trait ToTerritory {
|
||||
fn to_territory(&self) -> Territory;
|
||||
}
|
||||
|
||||
pub trait UnsplitGeo {
|
||||
fn unsplit_geo(self) -> FeatureCollection;
|
||||
}
|
||||
|
||||
pub trait ToMultiPolygon {
|
||||
fn to_mp(&self) -> MultiPolygon;
|
||||
}
|
||||
|
|
97
src/utils.rs
97
src/utils.rs
|
@ -1,10 +1,12 @@
|
|||
use std::{collections::HashMap, fs, path::Path};
|
||||
use std::{fs, path::Path};
|
||||
|
||||
use geo::{BooleanOps, Geometry, MultiPolygon};
|
||||
use geojson::{Feature, FeatureCollection, GeoJson};
|
||||
use serde_json::json;
|
||||
use geo::{BooleanOps, MultiPolygon};
|
||||
use geojson::{FeatureCollection, GeoJson};
|
||||
|
||||
use crate::types::{Config, CountryConfig, CountryData, Marker, MarkerType, Territory};
|
||||
use crate::types::{
|
||||
Config, CountryConfig, CountryData, Territory, ToCountryFeature, ToMultiPolygon, ToSplitGeo,
|
||||
UnsplitGeo,
|
||||
};
|
||||
|
||||
pub fn read_config() -> Config {
|
||||
let c = toml::from_str::<Config>(&fs::read_to_string("config.toml").unwrap());
|
||||
|
@ -47,88 +49,21 @@ pub fn dissolve_territory(
|
|||
id: String,
|
||||
config: CountryConfig,
|
||||
) -> FeatureCollection {
|
||||
let mut markers: Vec<Marker> = vec![];
|
||||
let mut territories: Vec<Territory> = vec![];
|
||||
let (markers, territories) = geo.split_geo();
|
||||
|
||||
geo.features.iter().for_each(|f| {
|
||||
let properties = f.properties.clone().unwrap();
|
||||
|
||||
let geometry: Geometry = f.geometry.clone().unwrap().try_into().unwrap();
|
||||
|
||||
match geometry {
|
||||
Geometry::Point(p) => {
|
||||
let ty = match properties
|
||||
.get("type")
|
||||
.expect("Missing marker type")
|
||||
.to_string()
|
||||
.trim_matches('"')
|
||||
{
|
||||
"capital" | "capital-city" => MarkerType::Capital,
|
||||
"city" => MarkerType::City,
|
||||
"landmark" => MarkerType::Landmark,
|
||||
|
||||
t => panic!("Invalid marker type: {}", t),
|
||||
};
|
||||
|
||||
markers.push(Marker {
|
||||
coordinates: p,
|
||||
title: properties
|
||||
.get("title")
|
||||
.expect("Missing marker title")
|
||||
.to_string(),
|
||||
description: properties
|
||||
.get("description")
|
||||
.unwrap_or(&json!(""))
|
||||
.to_string(),
|
||||
ty,
|
||||
})
|
||||
}
|
||||
|
||||
Geometry::MultiPolygon(mp) => territories.push(Territory::MultiPolygon(mp)),
|
||||
|
||||
Geometry::Polygon(p) => territories.push(Territory::Polygon(p)),
|
||||
|
||||
_ => panic!("Unexpected geometry type"),
|
||||
}
|
||||
});
|
||||
|
||||
let territories = territories.iter();
|
||||
|
||||
let dissolved = territories.fold(MultiPolygon::new(vec![]), |a, b| match b {
|
||||
Territory::Polygon(p) => a.union(&MultiPolygon::new(vec![p.clone()])),
|
||||
let dissolved = territories
|
||||
.iter()
|
||||
.fold(MultiPolygon::new(vec![]), |a, b| match b {
|
||||
Territory::Polygon(p) => a.union(&p.to_mp()),
|
||||
Territory::MultiPolygon(mp) => a.union(mp),
|
||||
});
|
||||
})
|
||||
.to_country_feature(&id, &config);
|
||||
|
||||
// combine markers and dissolved
|
||||
let mut features: Vec<Feature> = markers.iter().map(|m| m.to_feature()).collect();
|
||||
|
||||
features.push(geojson::Feature {
|
||||
geometry: Some(geojson::Geometry::new((&dissolved).into())),
|
||||
properties: Some(
|
||||
serde_json::Map::from_iter([
|
||||
("id".to_owned(), json!(id)),
|
||||
("type".to_owned(), json!("country")),
|
||||
("fill".to_owned(), json!(config.fill)),
|
||||
("stroke".to_owned(), json!(config.stroke)),
|
||||
("tags".to_owned(), json!(config.tags)),
|
||||
])
|
||||
.into(),
|
||||
),
|
||||
|
||||
bbox: None,
|
||||
id: None,
|
||||
foreign_members: None,
|
||||
});
|
||||
|
||||
FeatureCollection {
|
||||
features,
|
||||
bbox: None,
|
||||
foreign_members: None,
|
||||
}
|
||||
(markers, dissolved).unsplit_geo()
|
||||
}
|
||||
|
||||
pub fn hash_hex_color(s: String) -> String {
|
||||
let hex_str = format!("{:x}", xxhash_rust::xxh3::xxh3_64(s.as_bytes()));
|
||||
|
||||
format!("#{:6}", hex_str.chars().take(6).collect::<String>())
|
||||
format!("#{}", hex_str.chars().take(6).collect::<String>())
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue