Expand description
§Introduction
This crate helps you read and write GeoJSON — a format for encoding geographic data structures.
To get started, add geojson
to your Cargo.toml
.
cargo add geojson
§Types and crate structure
This crate is structured around the GeoJSON spec (IETF RFC 7946),
and users are encouraged to familiarise themselves with it. The elements specified in this spec
have corresponding struct and type definitions in this crate, e.g. FeatureCollection
, Feature
,
etc.
There are two primary ways to use this crate.
The first, most general, approach is to write your code to deal in terms of these structs from the GeoJSON spec. This allows you to access the full expressive power of GeoJSON with the speed and safety of Rust.
Alternatively, and commonly, if you only need geometry and properties (and not, e.g.
foreign members), you can bring your own
types, and use this crate’s serde
integration to serialize and deserialize your custom
types directly to and from a GeoJSON Feature Collection. See more on using your own types with
serde.
If you want to use GeoJSON as input to or output from a geometry processing crate like
geo
, see the section on using geojson with
geo-types.
§Using structs from the GeoJSON spec
A GeoJSON object can be one of three top-level objects, reflected in this crate as the
GeoJson
enum members of the same name.
- A
Geometry
represents points, curves, and surfaces in coordinate space. - A
Feature
usually contains aGeometry
and some associated data, for example a “name” field or any other properties you’d like associated with theGeometry
. - A
FeatureCollection
is a list ofFeature
s.
Because Feature
and FeatureCollection
are more flexible, bare Geometry
GeoJSON
documents are rarely encountered in the wild. As such, conversions from Geometry
or Geometry Value
to Feature
objects are provided via the From
trait.
Beware: A common point of confusion arises when converting a GeoJson
GeometryCollection
. Do you want it converted to a single
Feature
whose geometry is a GeometryCollection
, or do you
want a FeatureCollection
with each element of the
GeometryCollection
converted to its own Feature
, potentially
with their own individual properties. Either is possible, but it’s important you understand
which one you want.
§Examples
§Reading
GeoJson
can be deserialized by calling str::parse
:
use geojson::{Feature, GeoJson, Geometry, Value};
use std::convert::TryFrom;
let geojson_str = r#"
{
"type": "Feature",
"properties": { "food": "donuts" },
"geometry": {
"type": "Point",
"coordinates": [ -118.2836, 34.0956 ]
}
}
"#;
let geojson: GeoJson = geojson_str.parse::<GeoJson>().unwrap();
let feature: Feature = Feature::try_from(geojson).unwrap();
// read property data
assert_eq!("donuts", feature.property("food").unwrap());
// read geometry data
let geometry: Geometry = feature.geometry.unwrap();
if let Value::Point(coords) = geometry.value {
assert_eq!(coords, vec![-118.2836, 34.0956]);
}
§Writing
GeoJson
can be serialized by calling to_string
:
use geojson::{Feature, GeoJson, Geometry, Value};
let geometry = Geometry::new(Value::Point(vec![-120.66029, 35.2812]));
let geojson = GeoJson::Feature(Feature {
bbox: None,
geometry: Some(geometry),
id: None,
// See the next section about Feature properties
properties: Some(get_properties()),
foreign_members: None,
});
let geojson_string = geojson.to_string();
§Feature properties
The geojson
crate is built on top of serde_json
. Consequently,
some fields like feature.properties
hold serde_json
values.
use geojson::{JsonObject, JsonValue};
let mut properties = JsonObject::new();
let key = "name".to_string();
properties.insert(key, JsonValue::from("Firestone Grill"));
§Parsing
GeoJSON’s spec is quite simple, but it has several subtleties that must be taken into account when parsing it:
- The
geometry
field of aFeature
is anOption
— it can be blank. GeometryCollection
s contain otherGeometry
objects, and can nest.- We strive to produce strictly valid output, but we are more permissive about what we accept as input.
Here’s a minimal example which will parse and process a GeoJSON string.
use geojson::{GeoJson, Geometry, Value};
/// Process top-level GeoJSON Object
fn process_geojson(gj: &GeoJson) {
match *gj {
GeoJson::FeatureCollection(ref ctn) => {
for feature in &ctn.features {
if let Some(ref geom) = feature.geometry {
process_geometry(geom)
}
}
}
GeoJson::Feature(ref feature) => {
if let Some(ref geom) = feature.geometry {
process_geometry(geom)
}
}
GeoJson::Geometry(ref geometry) => process_geometry(geometry),
}
}
/// Process GeoJSON geometries
fn process_geometry(geom: &Geometry) {
match geom.value {
Value::Polygon(_) => println!("Matched a Polygon"),
Value::MultiPolygon(_) => println!("Matched a MultiPolygon"),
Value::GeometryCollection(ref gc) => {
println!("Matched a GeometryCollection");
// !!! GeometryCollections contain other Geometry types, and can
// nest — we deal with this by recursively processing each geometry
for geometry in gc {
process_geometry(geometry)
}
}
// Point, LineString, and their Multi– counterparts
_ => println!("Matched some other geometry"),
}
}
fn main() {
let geojson_str = r#"
{
"type": "GeometryCollection",
"geometries": [
{"type": "Point", "coordinates": [0,1]},
{"type": "MultiPoint", "coordinates": [[-1,0],[1,0]]},
{"type": "LineString", "coordinates": [[-1,-1],[1,-1]]},
{"type": "MultiLineString", "coordinates": [
[[-2,-2],[2,-2]],
[[-3,-3],[3,-3]]
]},
{"type": "Polygon", "coordinates": [
[[-5,-5],[5,-5],[0,5],[-5,-5]],
[[-4,-4],[4,-4],[0,4],[-4,-4]]
]},
{ "type": "MultiPolygon", "coordinates": [[
[[-7,-7],[7,-7],[0,7],[-7,-7]],
[[-6,-6],[6,-6],[0,6],[-6,-6]]
],[
[[-9,-9],[9,-9],[0,9],[-9,-9]],
[[-8,-8],[8,-8],[0,8],[-8,-8]]]
]},
{"type": "GeometryCollection", "geometries": [
{"type": "Polygon", "coordinates": [
[[-5.5,-5.5],[5,-5],[0,5],[-5,-5]],
[[-4,-4],[4,-4],[0,4],[-4.5,-4.5]]
]}
]}
]
}
"#;
let geojson = geojson_str.parse::<GeoJson>().unwrap();
process_geojson(&geojson);
}
§Use geojson with other crates by converting to geo-types
geo-types
are a common geometry format used across many
geospatial processing crates. The geo-types
feature is enabled by default.
§Convert geo-types
to geojson
From
is implemented on the Value
enum variants to allow conversion from geo-types
Geometries.
// requires enabling the `geo-types` feature
let geo_point: geo_types::Point<f64> = geo_types::Point::new(2., 9.);
let geo_geometry: geo_types::Geometry<f64> = geo_types::Geometry::from(geo_point);
assert_eq!(
geojson::Value::from(&geo_point),
geojson::Value::Point(vec![2., 9.]),
);
assert_eq!(
geojson::Value::from(&geo_geometry),
geojson::Value::Point(vec![2., 9.]),
);
If you wish to produce a FeatureCollection
from a homogeneous collection of geo-types
, a
From
impl is provided for geo_types::GeometryCollection
:
// requires enabling the `geo-types` feature
use geojson::FeatureCollection;
use geo_types::{polygon, point, Geometry, GeometryCollection};
use std::iter::FromIterator;
let poly: Geometry<f64> = polygon![
(x: -111., y: 45.),
(x: -111., y: 41.),
(x: -104., y: 41.),
(x: -104., y: 45.),
].into();
let point: Geometry<f64> = point!(x: 1.0, y: 2.0).into();
let geometry_collection = GeometryCollection::from_iter(vec![poly, point]);
let feature_collection = FeatureCollection::from(&geometry_collection);
assert_eq!(2, feature_collection.features.len());
§Convert geojson
to geo-types
The geo-types
feature implements the TryFrom
trait,
providing fallible conversions to geo-types Geometries
from GeoJson
, Value
, Feature
, FeatureCollection
or Geometry
types.
§Convert geojson
to geo_types::Geometry<f64>
// This example requires the `geo-types` feature
use geo_types::Geometry;
use geojson::GeoJson;
use std::convert::TryFrom;
use std::str::FromStr;
let geojson_str = r#"
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-0.13583511114120483,
51.5218870403801
]
}
}
"#;
let geojson = GeoJson::from_str(geojson_str).unwrap();
// Turn the GeoJSON string into a geo_types Geometry
let geom = geo_types::Geometry::<f64>::try_from(geojson).unwrap();
§Caveats
- Round-tripping with intermediate processing using the
geo
types may not produce identical output, as e.g. outerPolygon
rings are automatically closed. geojson
attempts to output valid geometries. In particular, it may re-orientPolygon
rings when serialising.
The geojson_example
and
polylabel_cmd
crates contain example
implementations which may be useful if you wish to perform this kind of processing yourself and require
more granular control over performance and / or memory allocation.
§Using your own types with serde
If your use case is simple enough, you can read and write GeoJSON directly to and from your own types using serde.
Specifically, the requirements are:
- Your type has a
geometry
field.- If your
geometry
field is ageo-types
Geometry, you must use the providedserialize_with
/deserialize_with
helpers. - Otherwise, your
geometry
field must be acrate::Geometry
.
- If your
- Other than
geometry
, you may only use a Feature’sproperties
- all other fields, like foreign members, will be lost.
#[derive(serde::Serialize, serde::Deserialize)]
struct MyStruct {
// Serialize as geojson, rather than using the type's default serialization
#[serde(serialize_with = "serialize_geometry", deserialize_with = "deserialize_geometry")]
geometry: geo_types::Point<f64>,
name: String,
count: u64,
}
See more in the serialization and deserialization modules.
Re-exports§
Modules§
- de
- Build your struct from GeoJSON using
serde
- errors
- Module for all GeoJSON-related errors
- feature
- ser
- Write your struct to GeoJSON using
serde
Structs§
- Feature
- Feature Objects
- Feature
Collection - Feature Collection Objects
- Feature
Reader - Enumerates individual Features from a GeoJSON FeatureCollection
- Feature
Writer - Write Features to a FeatureCollection
- Geometry
- Geometry Objects
Enums§
Functions§
- quick_
collection Deprecated - A shortcut for producing
geo_types
GeometryCollection objects from arbitrary valid GeoJSON input.
Type Aliases§
- Bbox
- Bounding Boxes
- Json
Object - Json
Value - Line
String Type - Point
Type - Polygon
Type - Position
- Positions