gistools/readers/shapefile/
mmap.rs

1use super::{DataBaseFile, Definition, ShapeFileReader};
2use crate::{parsers::MMapReader, proj::Transformer};
3use s2json::MValueCompatible;
4use std::{
5    collections::BTreeMap,
6    fs::{File, exists},
7    io::Read,
8    path::Path,
9    string::{String, ToString},
10};
11
12/// # Build a Shapefile from an input path
13///
14/// ## Description
15/// Given a path to where all the shapefile relevant files exist, build a [`ShapeFileReader`]
16///
17/// ## Usage
18/// ```rust
19/// use gistools::{parsers::{MMapReader, FeatureReader}, readers::{ShapeFileReader, mmap::shapefile_from_path}};
20/// use s2json::MValue;
21/// use std::{collections::BTreeMap, path::PathBuf};
22///
23/// let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
24/// path.push("tests/readers/shapefile/fixtures/utf.shp");
25/// let path_str = path.to_str().unwrap();
26///
27/// #[derive(Default, Debug, Clone, MValue, PartialEq)]
28/// struct Props {
29///     field: String,
30/// }
31///
32/// let shp: ShapeFileReader<MMapReader, Props> =
33///     shapefile_from_path(path_str, BTreeMap::from([("a".into(), "b".into())]));
34///
35/// let features: Vec<_> = shp.iter().collect();
36/// assert_eq!(features.len(), 2);
37/// ```
38pub fn shapefile_from_path<I: AsRef<Path> + ToString, P: MValueCompatible>(
39    input: I,
40    epsg_codes: BTreeMap<String, String>,
41) -> ShapeFileReader<MMapReader, P> {
42    let path = input.to_string().replace(".shp", "");
43    let shp = path.clone() + ".shp";
44    let dbf_str = path.clone() + ".dbf";
45    let prj_str = path.clone() + ".prj";
46    let cpg_str = path.clone() + ".cpg";
47
48    if exists(&shp).is_err() {
49        panic!("Shapefile does not exist");
50    }
51    let dbf: Option<String> = if exists(&dbf_str).is_ok() { Some(dbf_str) } else { None };
52    let prj: Option<String> = if exists(&prj_str).is_ok() { Some(prj_str) } else { None };
53    let cpg: Option<String> = if exists(&cpg_str).is_ok() { Some(cpg_str) } else { None };
54    let definition = Definition { shp, dbf, prj, cpg };
55
56    shapefile_from_definition(definition, epsg_codes)
57}
58
59/// # Build a Shapefile from a Definition
60///
61/// ## Description
62/// Given a collection of files, build a Shapefile
63pub fn shapefile_from_definition<P: MValueCompatible>(
64    def: Definition,
65    epsg_codes: BTreeMap<String, String>,
66) -> ShapeFileReader<MMapReader, P> {
67    let Definition { shp, dbf, prj, cpg } = def;
68    let mut database_file = None;
69    let mut encoding = None;
70    let mut transform = None;
71    if let Some(cpg) = cpg {
72        // read cpg file as string
73        let mut file = File::open(cpg).unwrap();
74        let mut input_str: String = String::new();
75        let _ = file.read_to_string(&mut input_str);
76        if !input_str.is_empty() {
77            encoding = Some(input_str);
78        }
79    }
80    // Handle projection
81    if let Some(prj) = prj {
82        let mut transformer = Transformer::new();
83        for (code, value) in epsg_codes.iter() {
84            transformer.insert_epsg_code(code.clone(), value.clone());
85        }
86        let pr_str = std::fs::read_to_string(prj).unwrap();
87        transformer.set_source(pr_str);
88        transform = Some(transformer);
89    }
90    // handle database data
91    if let Some(dbf) = dbf {
92        database_file = Some(DataBaseFile::new(MMapReader::from(dbf), encoding));
93    }
94
95    ShapeFileReader::new(MMapReader::from(shp), database_file, transform)
96}