gistools/readers/shapefile/
file.rs

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