#![deny(missing_docs)]
mod crs;
pub mod error;
pub mod feature;
pub mod flatgeobuf;
pub mod geojson;
pub mod geometry;
pub mod geopackage;
#[cfg(feature = "geoparquet")]
pub mod geoparquet;
pub mod gml;
pub mod gpx;
pub mod kml;
#[cfg(feature = "kmz")]
pub mod kmz;
pub mod mapinfo;
#[cfg(feature = "osmpbf")]
pub mod osmpbf;
pub mod reproject;
pub mod shapefile;
pub use error::{GeoError, Result};
pub use feature::{Crs, Feature, FieldDef, FieldType, FieldValue, Layer, Schema};
pub use geometry::{BBox, Coord, Geometry, GeometryType, Ring};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VectorFormat {
FlatGeobuf,
GeoJson,
GeoPackage,
#[cfg(feature = "geoparquet")]
GeoParquet,
Gml,
Gpx,
Kml,
#[cfg(feature = "kmz")]
Kmz,
MapInfoMif,
#[cfg(feature = "osmpbf")]
OsmPbf,
Shapefile,
}
impl VectorFormat {
pub fn detect<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
let path = path.as_ref();
let file_name_lc = path
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("")
.to_ascii_lowercase();
let ext_lc = path
.extension()
.and_then(|s| s.to_str())
.unwrap_or("")
.to_ascii_lowercase();
if file_name_lc.ends_with(".osm.pbf") {
#[cfg(feature = "osmpbf")]
{
return Ok(Self::OsmPbf);
}
#[cfg(not(feature = "osmpbf"))]
{
return Err(GeoError::NotImplemented(
"OSM PBF support requires enabling the `osmpbf` feature".into(),
));
}
}
match ext_lc.as_str() {
"fgb" => return Ok(Self::FlatGeobuf),
"geojson" => return Ok(Self::GeoJson),
"gpkg" => return Ok(Self::GeoPackage),
"gml" => return Ok(Self::Gml),
"gpx" => return Ok(Self::Gpx),
"kml" => return Ok(Self::Kml),
"mif" => return Ok(Self::MapInfoMif),
"shp" => return Ok(Self::Shapefile),
"parquet" => {
#[cfg(feature = "geoparquet")]
{
return Ok(Self::GeoParquet);
}
#[cfg(not(feature = "geoparquet"))]
{
return Err(GeoError::NotImplemented(
"GeoParquet support requires enabling the `geoparquet` feature".into(),
));
}
}
"kmz" => {
#[cfg(feature = "kmz")]
{
return Ok(Self::Kmz);
}
#[cfg(not(feature = "kmz"))]
{
return Err(GeoError::NotImplemented(
"KMZ support requires enabling the `kmz` feature".into(),
));
}
}
"json" => return Ok(Self::GeoJson),
"xml" => {
if let Some(kind) = sniff_xml(path)? {
return Ok(kind);
}
}
_ => {}
}
if path.extension().is_none() {
let shp = path.with_extension("shp");
if shp.exists() {
return Ok(Self::Shapefile);
}
}
if path.is_file() {
let sig = read_signature(path, 16)?;
if sig.starts_with(&flatgeobuf::MAGIC) {
return Ok(Self::FlatGeobuf);
}
if sig.starts_with(b"SQLite format 3\0") {
return Ok(Self::GeoPackage);
}
if sig.starts_with(b"PAR1") {
#[cfg(feature = "geoparquet")]
{
return Ok(Self::GeoParquet);
}
}
if sig.starts_with(b"PK\x03\x04") {
#[cfg(feature = "kmz")]
{
return Ok(Self::Kmz);
}
}
if let Some(kind) = sniff_xml(path)? {
return Ok(kind);
}
}
Err(GeoError::UnknownFormat(path.display().to_string()))
}
pub fn read<P: AsRef<std::path::Path>>(&self, path: P) -> Result<Layer> {
match self {
Self::FlatGeobuf => flatgeobuf::read(path),
Self::GeoJson => geojson::read(path),
Self::GeoPackage => geopackage::read(path),
#[cfg(feature = "geoparquet")]
Self::GeoParquet => geoparquet::read(path),
Self::Gml => gml::read(path),
Self::Gpx => gpx::read(path),
Self::Kml => kml::read(path),
#[cfg(feature = "kmz")]
Self::Kmz => kmz::read(path),
Self::MapInfoMif => mapinfo::read(path),
#[cfg(feature = "osmpbf")]
Self::OsmPbf => osmpbf::read(path),
Self::Shapefile => shapefile::read(path),
}
}
pub fn write<P: AsRef<std::path::Path>>(&self, layer: &Layer, path: P) -> Result<()> {
match self {
Self::FlatGeobuf => flatgeobuf::write(layer, path),
Self::GeoJson => geojson::write(layer, path),
Self::GeoPackage => geopackage::write(layer, path),
#[cfg(feature = "geoparquet")]
Self::GeoParquet => geoparquet::write(layer, path),
Self::Gml => gml::write(layer, path),
Self::Gpx => gpx::write(layer, path),
Self::Kml => kml::write(layer, path),
#[cfg(feature = "kmz")]
Self::Kmz => kmz::write(layer, path),
Self::MapInfoMif => mapinfo::write(layer, path),
#[cfg(feature = "osmpbf")]
Self::OsmPbf => Err(GeoError::NotImplemented(
"OSM PBF writer is not implemented".into(),
)),
Self::Shapefile => shapefile::write(layer, path),
}
}
}
pub fn read<P: AsRef<std::path::Path>>(path: P) -> Result<Layer> {
let fmt = VectorFormat::detect(path.as_ref())?;
fmt.read(path)
}
pub fn write<P: AsRef<std::path::Path>>(layer: &Layer, path: P, format: VectorFormat) -> Result<()> {
format.write(layer, path)
}
fn read_signature(path: &std::path::Path, n: usize) -> Result<Vec<u8>> {
use std::io::Read;
let mut f = std::fs::File::open(path)?;
let mut buf = vec![0u8; n];
let read_n = f.read(&mut buf)?;
buf.truncate(read_n);
Ok(buf)
}
fn sniff_xml(path: &std::path::Path) -> Result<Option<VectorFormat>> {
use std::io::Read;
let mut f = match std::fs::File::open(path) {
Ok(f) => f,
Err(_) => return Ok(None),
};
let mut buf = vec![0u8; 4096];
let n = f.read(&mut buf)?;
buf.truncate(n);
let txt = String::from_utf8_lossy(&buf).to_ascii_lowercase();
if txt.contains("<kml") {
return Ok(Some(VectorFormat::Kml));
}
if txt.contains("<gpx") {
return Ok(Some(VectorFormat::Gpx));
}
if txt.contains("<gml") || txt.contains("opengis.net/gml") {
return Ok(Some(VectorFormat::Gml));
}
Ok(None)
}