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
158
src/types.rs
158
src/types.rs
|
@ -1,5 +1,6 @@
|
||||||
use clap::{Parser, Subcommand};
|
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 geojson::{Feature, FeatureCollection, Value};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
@ -127,8 +128,8 @@ pub struct Marker {
|
||||||
pub ty: MarkerType,
|
pub ty: MarkerType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Marker {
|
impl ToFeature for Marker {
|
||||||
pub fn to_feature(&self) -> geojson::Feature {
|
fn to_feature(&self) -> geojson::Feature {
|
||||||
geojson::Feature {
|
geojson::Feature {
|
||||||
geometry: Some(geojson::Geometry::new(Value::Point(vec![
|
geometry: Some(geojson::Geometry::new(Value::Point(vec![
|
||||||
self.coordinates.x(),
|
self.coordinates.x(),
|
||||||
|
@ -171,25 +172,146 @@ pub enum Territory {
|
||||||
MultiPolygon(MultiPolygon),
|
MultiPolygon(MultiPolygon),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Territory {
|
impl ToTerritory for MultiPolygon {
|
||||||
pub fn to_feature(&self) -> geojson::Feature {
|
fn to_territory(&self) -> Territory {
|
||||||
match self {
|
Territory::MultiPolygon(self.clone())
|
||||||
Territory::Polygon(p) => geojson::Feature {
|
}
|
||||||
geometry: Some(geojson::Geometry::new(p.into())),
|
}
|
||||||
properties: None,
|
|
||||||
|
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,
|
bbox: None,
|
||||||
id: None,
|
id: None,
|
||||||
foreign_members: None,
|
foreign_members: None,
|
||||||
},
|
|
||||||
Territory::MultiPolygon(mp) => geojson::Feature {
|
|
||||||
geometry: Some(geojson::Geometry::new(mp.into())),
|
|
||||||
properties: None,
|
|
||||||
|
|
||||||
bbox: None,
|
|
||||||
id: None,
|
|
||||||
foreign_members: 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,
|
||||||
|
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 geo::{BooleanOps, MultiPolygon};
|
||||||
use geojson::{Feature, FeatureCollection, GeoJson};
|
use geojson::{FeatureCollection, GeoJson};
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
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 {
|
pub fn read_config() -> Config {
|
||||||
let c = toml::from_str::<Config>(&fs::read_to_string("config.toml").unwrap());
|
let c = toml::from_str::<Config>(&fs::read_to_string("config.toml").unwrap());
|
||||||
|
@ -47,88 +49,21 @@ pub fn dissolve_territory(
|
||||||
id: String,
|
id: String,
|
||||||
config: CountryConfig,
|
config: CountryConfig,
|
||||||
) -> FeatureCollection {
|
) -> FeatureCollection {
|
||||||
let mut markers: Vec<Marker> = vec![];
|
let (markers, territories) = geo.split_geo();
|
||||||
let mut territories: Vec<Territory> = vec![];
|
|
||||||
|
|
||||||
geo.features.iter().for_each(|f| {
|
let dissolved = territories
|
||||||
let properties = f.properties.clone().unwrap();
|
.iter()
|
||||||
|
.fold(MultiPolygon::new(vec![]), |a, b| match b {
|
||||||
let geometry: Geometry = f.geometry.clone().unwrap().try_into().unwrap();
|
Territory::Polygon(p) => a.union(&p.to_mp()),
|
||||||
|
|
||||||
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()])),
|
|
||||||
Territory::MultiPolygon(mp) => a.union(mp),
|
Territory::MultiPolygon(mp) => a.union(mp),
|
||||||
});
|
})
|
||||||
|
.to_country_feature(&id, &config);
|
||||||
|
|
||||||
// combine markers and dissolved
|
(markers, dissolved).unsplit_geo()
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash_hex_color(s: String) -> String {
|
pub fn hash_hex_color(s: String) -> String {
|
||||||
let hex_str = format!("{:x}", xxhash_rust::xxh3::xxh3_64(s.as_bytes()));
|
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