osm_pbf2json 0.2.2

A parser/filter/processor for OSM protobuf bundles
Documentation
use super::geo::Length;
use super::geojson::{Entity, Geometry};
use super::items::{AdminBoundary, Object, Street};
use rand::random;
use serde::{Deserialize, Serialize};
use serde_json::to_string;
use std::collections::HashMap;
use std::error::Error;
use std::io::Write;

pub trait Output {
    fn write_geojson(&self, writer: &mut dyn Write) -> Result<(), Box<dyn Error>>;
    fn write_json_lines(&self, writer: &mut dyn Write) -> Result<(), Box<dyn Error>>;
}

#[derive(Serialize, Deserialize)]
struct JSONBBox {
    sw: [f64; 2],
    ne: [f64; 2],
}

#[derive(Serialize, Deserialize)]
struct JSONBoundary {
    name: String,
    admin_level: u8,
    bbox: JSONBBox,
}

impl Output for Vec<AdminBoundary> {
    fn write_json_lines(&self, writer: &mut dyn Write) -> Result<(), Box<dyn Error>> {
        for boundary in self.iter() {
            let name = boundary.name.clone();
            let admin_level = boundary.admin_level;
            let (sw, ne) = boundary.geometry.sw_ne();
            let bbox = JSONBBox { sw, ne };
            let json_boundary = JSONBoundary {
                name,
                admin_level,
                bbox,
            };
            let json = to_string(&json_boundary)?;
            writeln!(writer, "{}", json)?;
        }
        Ok(())
    }

    fn write_geojson(&self, writer: &mut dyn Write) -> Result<(), Box<dyn Error>> {
        let features = self
            .iter()
            .map(|boundary| {
                let coordinates = boundary.geometry.coordinates();
                let geometry = Geometry::MultiPolygon { coordinates };
                let properties = vec![
                    (String::from("name"), boundary.name.clone()),
                    (
                        String::from("admin_level"),
                        boundary.admin_level.to_string(),
                    ),
                ]
                .into_iter()
                .collect();
                Entity::Feature {
                    geometry,
                    properties,
                }
            })
            .collect();
        let feature_collection = Entity::FeatureCollection { features };
        let string = to_string(&feature_collection)?;
        writeln!(writer, "{}", string)?;
        Ok(())
    }
}

#[derive(Serialize, Deserialize)]
struct JSONStreet {
    id: i64,
    name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    boundary: Option<String>,
    length: f64,
    loc: (f64, f64),
}

impl Output for Vec<Object> {
    fn write_json_lines(&self, writer: &mut dyn Write) -> Result<(), Box<dyn Error>> {
        for object in self.iter() {
            let json = match object {
                Object::Node(node) => to_string(node),
                Object::Way(way) => to_string(way),
                Object::Relation(rel) => to_string(rel),
            }?;
            writeln!(writer, "{}", json)?;
        }
        Ok(())
    }

    fn write_geojson(&self, _writer: &mut dyn Write) -> Result<(), Box<dyn Error>> {
        unimplemented!();
    }
}

impl Output for Vec<Street> {
    fn write_json_lines(&self, writer: &mut dyn Write) -> Result<(), Box<dyn Error>> {
        for street in self.iter() {
            let id = street.id();
            let loc = street.middle().ok_or("could not calculate middle")?;
            let name = street.name.clone();
            let boundary = street.boundary.clone();
            let length = street.length();
            let json_street = JSONStreet {
                id,
                name,
                boundary,
                length,
                loc,
            };
            let json = to_string(&json_street)?;
            writeln!(writer, "{}", json)?;
        }
        Ok(())
    }

    fn write_geojson(&self, writer: &mut dyn Write) -> Result<(), Box<dyn Error>> {
        let features = self
            .iter()
            .filter_map(|street| {
                let geometries: Vec<_> = street
                    .segments
                    .iter()
                    .filter(|segment| segment.geometry.len() >= 2)
                    .map(|segment| segment.geometry.clone())
                    .collect();
                if geometries.is_empty() {
                    return None;
                }
                let coordinates = geometries.iter().map(|g| g.into()).collect();
                let geometry = Geometry::MultiLineString { coordinates };
                let r = random::<u8>();
                let g = random::<u8>();
                let b = random::<u8>();
                let random_color = format!("#{:02X}{:02X}{:02X}", r, g, b);
                let mut properties: HashMap<String, String> = HashMap::new();
                properties.insert("name".into(), street.name.clone());
                properties.insert("stroke".into(), random_color);
                if let Some(name) = &street.boundary {
                    properties.insert("boundary".into(), name.clone());
                }
                let entity = Entity::Feature {
                    geometry,
                    properties,
                };
                Some(entity)
            })
            .collect();

        let feature_collection = Entity::FeatureCollection { features };
        let string = to_string(&feature_collection)?;
        writeln!(writer, "{}", string)?;
        Ok(())
    }
}