From cf490b71ab2d42204ae89679e8380255c9fa9da8 Mon Sep 17 00:00:00 2001 From: Niklas Klein Date: Fri, 21 Jul 2023 13:56:10 +0200 Subject: [PATCH] One GeoJson type to rule them all --- README.md | 7 ++++++ .../main/scala/io/taig/geojson/circe.scala | 25 +++++++++++++++---- .../main/scala/io/taig/geojson/Feature.scala | 5 ---- .../io/taig/geojson/FeatureCollection.scala | 10 -------- .../geojson/{Geometry.scala => GeoJson.scala} | 20 ++++++++++++++- 5 files changed, 46 insertions(+), 21 deletions(-) delete mode 100644 modules/core/src/main/scala/io/taig/geojson/Feature.scala delete mode 100644 modules/core/src/main/scala/io/taig/geojson/FeatureCollection.scala rename modules/core/src/main/scala/io/taig/geojson/{Geometry.scala => GeoJson.scala} (88%) diff --git a/README.md b/README.md index db7ad85..f301816 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,10 @@ # GeoJSON > Plain GeoJSON data structures + +```scala +libraryDependencies ++= + "io.taig" %%% "geojson-core" % "x.y.z" :: + "io.taig" %%% "geojson-circe" % "x.y.z" :: + Nil +``` diff --git a/modules/circe/src/main/scala/io/taig/geojson/circe.scala b/modules/circe/src/main/scala/io/taig/geojson/circe.scala index e5ac703..2627e5c 100644 --- a/modules/circe/src/main/scala/io/taig/geojson/circe.scala +++ b/modules/circe/src/main/scala/io/taig/geojson/circe.scala @@ -7,11 +7,21 @@ import io.circe.JsonObject import io.circe.DecodingFailure trait circe: + implicit val decodeGeoJson: Decoder[GeoJson] = cursor => + cursor + .get[String]("type") + .flatMap: + case FeatureCollection.Type => cursor.as[FeatureCollection] + case Feature.Type => cursor.as[Feature] + case _ => cursor.as[Geometry] + + implicit val encodeGeoJson: Encoder.AsObject[GeoJson] = + case geoJson: FeatureCollection => geoJson.asJsonObject + case geoJson: Feature => geoJson.asJsonObject + case geoJson: Geometry => geoJson.asJsonObject + implicit final val decodeFeatureCollection: Decoder[FeatureCollection] = cursor => - for - _ <- cursor.get["FeatureCollection"]("type") - features <- cursor.get[List[Feature]]("features") - yield FeatureCollection(features) + cursor.get[List[Feature]]("features").map(FeatureCollection.apply) implicit final val encodeFeatureCollection: Encoder.AsObject[FeatureCollection] = feature => JsonObject( @@ -21,7 +31,6 @@ trait circe: implicit final val decodeFeature: Decoder[Feature] = cursor => for - _ <- cursor.get["Feature"]("type") id <- cursor.get[Option[String]]("id") geometry <- cursor.get[Option[Geometry]]("geometry") properties <- cursor.get[Option[Map[String, String]]]("properties") @@ -40,6 +49,12 @@ trait circe: .get[String]("type") .flatMap: case GeometryCollection.Type => cursor.as[GeometryCollection] + case LineString.Type => cursor.as[LineString] + case MultiLineString.Type => cursor.as[MultiLineString] + case MultiPoint.Type => cursor.as[MultiPoint] + case MultiPolygon.Type => cursor.as[MultiPolygon] + case Point.Type => cursor.as[Point] + case Polygon.Type => cursor.as[Polygon] case tpe => DecodingFailure(s"Unknown type: $tpe", cursor.downField("type").history).asLeft implicit final val encodeGeometry: Encoder.AsObject[Geometry] = diff --git a/modules/core/src/main/scala/io/taig/geojson/Feature.scala b/modules/core/src/main/scala/io/taig/geojson/Feature.scala deleted file mode 100644 index d5854f6..0000000 --- a/modules/core/src/main/scala/io/taig/geojson/Feature.scala +++ /dev/null @@ -1,5 +0,0 @@ -package io.taig.geojson - -final case class Feature(id: Option[String], geometry: Option[Geometry], properties: Option[Map[String, String]]): - def combine(feature: Feature): FeatureCollection = FeatureCollection(List(this, feature)) - def toFeatureCollection: FeatureCollection = FeatureCollection(List(this)) diff --git a/modules/core/src/main/scala/io/taig/geojson/FeatureCollection.scala b/modules/core/src/main/scala/io/taig/geojson/FeatureCollection.scala deleted file mode 100644 index 8291829..0000000 --- a/modules/core/src/main/scala/io/taig/geojson/FeatureCollection.scala +++ /dev/null @@ -1,10 +0,0 @@ -package io.taig.geojson - -opaque type FeatureCollection = List[Feature] - -object FeatureCollection: - extension (self: FeatureCollection) - def combine(featureCollection: FeatureCollection): FeatureCollection = self ++ featureCollection - def features: List[Feature] = self - - def apply(features: List[Feature]): FeatureCollection = features diff --git a/modules/core/src/main/scala/io/taig/geojson/Geometry.scala b/modules/core/src/main/scala/io/taig/geojson/GeoJson.scala similarity index 88% rename from modules/core/src/main/scala/io/taig/geojson/Geometry.scala rename to modules/core/src/main/scala/io/taig/geojson/GeoJson.scala index eb7cd76..e93b0b7 100644 --- a/modules/core/src/main/scala/io/taig/geojson/Geometry.scala +++ b/modules/core/src/main/scala/io/taig/geojson/GeoJson.scala @@ -1,6 +1,24 @@ package io.taig.geojson -sealed abstract class Geometry extends Product with Serializable: +sealed abstract class GeoJson extends Product with Serializable + +final case class FeatureCollection(features: List[Feature]) extends GeoJson: + def combine(featureCollection: FeatureCollection): FeatureCollection = FeatureCollection( + this.features ++ featureCollection.features + ) + +object FeatureCollection: + val Type: String = "FeatureCollection" + +final case class Feature(id: Option[String], geometry: Option[Geometry], properties: Option[Map[String, String]]) + extends GeoJson: + def combine(feature: Feature): FeatureCollection = FeatureCollection(List(this, feature)) + def toFeatureCollection: FeatureCollection = FeatureCollection(List(this)) + +object Feature: + val Type: String = "Feature" + +sealed abstract class Geometry extends GeoJson: def combine(geometry: Geometry): Geometry final def toGeometryCollection: GeometryCollection = this match