Skip to content

Commit c281e7f

Browse files
committed
Reorganize to include examples
1 parent 7442414 commit c281e7f

File tree

108 files changed

+651
-53
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+651
-53
lines changed

build.sbt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,22 @@ val vpExtraSettings = Seq(
147147
// micrositeBaseUrl := "/vectorpipe"
148148
// micrositeDocumentationUrl := "/vectorpipe/latest/api/#vectorpipe.package" /* Location of Scaladocs */
149149

150+
lazy val root = project
151+
.in(file("."))
152+
.aggregate(vectorpipe, examples)
153+
.settings(commonSettings, vpExtraSettings)
154+
150155
/* Main project */
151156
lazy val vectorpipe = project
152-
.in(file("."))
157+
.in(file("core"))
153158
.settings(commonSettings, vpExtraSettings, release)
154159

160+
/* Example projects */
161+
lazy val examples = project
162+
.in(file("examples"))
163+
.settings(commonSettings, vpExtraSettings)
164+
.dependsOn(vectorpipe)
165+
155166
/* Benchmarking suite.
156167
* Benchmarks can be executed by first switching to the `bench` project and then by running:
157168
jmh:run -t 1 -f 1 -wi 5 -i 5 .*Bench.*
@@ -162,7 +173,7 @@ lazy val bench = project
162173
.dependsOn(vectorpipe)
163174
.enablePlugins(JmhPlugin)
164175

165-
176+
onLoad in Global ~= (_ andThen ("project vectorpipe" :: _))
166177

167178

168179
// assemblyShadeRules in assembly := {

src/main/resources/META-INF/services/org.apache.spark.sql.sources.DataSourceRegister renamed to core/src/main/resources/META-INF/services/org.apache.spark.sql.sources.DataSourceRegister

File renamed without changes.
File renamed without changes.

src/main/scala/vectorpipe/VectorPipe.scala renamed to core/src/main/scala/vectorpipe/VectorPipe.scala

Lines changed: 56 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import org.apache.spark.sql.functions._
1515
import org.apache.spark.sql.types.StringType
1616
import org.locationtech.jts.{geom => jts}
1717

18+
import scala.reflect.ClassTag
19+
1820
object VectorPipe {
1921

2022
/** Vectortile conversion options.
@@ -46,7 +48,10 @@ object VectorPipe {
4648
def forAllZoomsWithSrcProjection(zoom: Int, crs: CRS) = Options(zoom, Some(0), crs, None)
4749
}
4850

49-
def apply(input: DataFrame, pipeline: vectortile.Pipeline, options: Options): Unit = {
51+
def apply[T: ClassTag](input: DataFrame, pipeline: vectortile.Pipeline, options: Options): Unit = {
52+
import input.sparkSession.implicits._
53+
import vectorpipe.encoders._
54+
5055
val geomColumn = pipeline.geometryColumn
5156
assert(input.columns.contains(geomColumn) &&
5257
input.schema(geomColumn).dataType.isInstanceOf[org.apache.spark.sql.jts.AbstractGeometryUDT[jts.Geometry]],
@@ -74,46 +79,49 @@ object VectorPipe {
7479
SpatialKey(k.col / 2, k.row / 2) }.toSeq
7580
}
7681

77-
def generateVectorTiles[G <: Geometry](df: DataFrame, level: LayoutLevel): RDD[(SpatialKey, VectorTile)] = {
82+
def generateVectorTiles[G <: Geometry](df: DataFrame, level: LayoutLevel): Dataset[(SpatialKey, Array[Byte])] = {
7883
val zoom = level.zoom
79-
val clip = udf { (g: jts.Geometry, key: GenericRowWithSchema) =>
80-
val k = getSpatialKey(key)
81-
pipeline.clip(g, k, level)
82-
}
8384

84-
val selectedGeometry = pipeline
85-
.select(df, zoom, keyColumn)
85+
val selectedGeometry = pipeline.select match {
86+
case None => df
87+
case Some(select) => select(df, zoom, keyColumn)
88+
}
8689

87-
val clipped = selectedGeometry
90+
val keyed = selectedGeometry
8891
.withColumn(keyColumn, explode(col(keyColumn)))
89-
.repartition(col(keyColumn)) // spread copies of possibly ill-tempered geometries around cluster prior to clipping
90-
.withColumn(geomColumn, clip(col(geomColumn), col(keyColumn)))
92+
93+
val clipped = pipeline.clip match {
94+
case None => keyed
95+
case Some(clipper) =>
96+
val clip = udf { (g: jts.Geometry, key: GenericRowWithSchema) =>
97+
val k = getSpatialKey(key)
98+
clipper(g, k, level)
99+
}
100+
val toClip = keyed.repartition(col(keyColumn)) // spread copies of possibly ill-tempered geometries around cluster prior to clipping
101+
toClip.withColumn(geomColumn, clip(col(geomColumn), col(keyColumn)))
102+
}
91103

92104
pipeline.layerMultiplicity match {
93105
case SingleLayer(layerName) =>
94106
clipped
95-
.rdd
96-
.map { r => (getSpatialKey(r, keyColumn), pipeline.pack(r, zoom)) }
97-
.groupByKey
98-
.map { case (key, feats) =>
107+
.map { r => SingleLayerEntry(getSpatialKey(r, keyColumn), pipeline.pack(r, zoom)) }
108+
.groupByKey(_.key)
109+
.mapGroups { (key: SpatialKey, sleIter: Iterator[SingleLayerEntry]) =>
99110
val ex = level.layout.mapTransform.keyToExtent(key)
100-
key -> buildVectorTile(feats, layerName, ex, options.tileResolution, options.orderAreas)
111+
key -> buildVectorTile(sleIter.map(_.feature).toIterable, layerName, ex, options.tileResolution, options.orderAreas).toBytes
101112
}
102113
case LayerNamesInColumn(layerNameCol) =>
103114
assert(selectedGeometry.schema(layerNameCol).dataType == StringType,
104115
s"layerMultiplicity=${pipeline.layerMultiplicity} requires String-type column of name ${layerNameCol}")
116+
105117
clipped
106-
.rdd
107-
.map { r => (getSpatialKey(r, keyColumn), r.getAs[String](layerNameCol) -> pipeline.pack(r, zoom)) }
108-
.groupByKey
109-
.mapPartitions{ iter: Iterator[(SpatialKey, Iterable[(String, VectorTileFeature[Geometry])])] =>
110-
iter.map{ case (key, groupedFeatures) => {
111-
val layerFeatures: Map[String, Iterable[VectorTileFeature[Geometry]]] =
112-
groupedFeatures.groupBy(_._1).mapValues(_.map(_._2))
113-
val ex = level.layout.mapTransform.keyToExtent(key)
114-
key -> buildVectorTile(layerFeatures, ex, options.tileResolution, options.orderAreas)
115-
}}
116-
}
118+
.map { r => MultipleLayerEntry(getSpatialKey(r, keyColumn), r.getAs[String](layerNameCol), pipeline.pack(r, zoom)) }
119+
.groupByKey(_.key)
120+
.mapGroups{ (key: SpatialKey, iter: Iterator[MultipleLayerEntry]) =>
121+
val ex = level.layout.mapTransform.keyToExtent(key)
122+
val layerFeatures = iter.toSeq.groupBy(_.layer).mapValues(_.map(_.feature))
123+
key -> buildVectorTile(layerFeatures, ex, options.tileResolution, options.orderAreas).toBytes
124+
}
117125
}
118126
}
119127

@@ -134,16 +142,30 @@ object VectorPipe {
134142
} else {
135143
df
136144
}
137-
val simplify = udf { g: jts.Geometry => pipeline.simplify(g, level.layout) }
138-
val reduced = pipeline
139-
.reduce(working, level, keyColumn)
140-
val prepared = reduced
141-
.withColumn(geomColumn, simplify(col(geomColumn)))
142-
val vts = generateVectorTiles(prepared, level)
145+
146+
val reduced = pipeline.reduce match {
147+
case None => working
148+
case Some(reduce) => reduce(working, level, keyColumn)
149+
}
150+
151+
val simplified = pipeline.simplify match {
152+
case None => reduced
153+
case Some(simplifier) =>
154+
val simplify = udf { g: jts.Geometry => simplifier(g, level.layout) }
155+
reduced.withColumn(geomColumn, simplify(col(geomColumn)))
156+
}
157+
158+
val vts = generateVectorTiles(simplified, level)
143159
saveVectorTiles(vts, zoom, pipeline.baseOutputURI)
144-
prepared.withColumn(keyColumn, reduceKeys(col(keyColumn)))
160+
161+
simplified.withColumn(keyColumn, reduceKeys(col(keyColumn)))
145162
}
146163

147164
}
148165

166+
private case class SingleLayerEntry(key: SpatialKey, feature: VectorTileFeature[Geometry])
167+
private case class MultipleLayerEntry(key: SpatialKey, layer: String, feature: VectorTileFeature[Geometry])
168+
169+
private implicit def sleEncoder: Encoder[SingleLayerEntry] = Encoders.kryo[SingleLayerEntry]
170+
private implicit def mleEncoder: Encoder[MultipleLayerEntry] = Encoders.kryo[MultipleLayerEntry]
149171
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package vectorpipe.encoders
2+
3+
import geotrellis.vector._
4+
import geotrellis.vectortile._
5+
import org.apache.spark.sql.{Encoder, Encoders}
6+
import org.apache.spark.sql.catalyst.encoders.ExpressionEncoder
7+
8+
object GTEncoders {
9+
implicit def gtGeometryEncoder: Encoder[Geometry] = Encoders.kryo[Geometry]
10+
implicit def gtPointEncoder: Encoder[Point] = ExpressionEncoder()
11+
implicit def gtMultiPointEncoder: Encoder[MultiPoint] = ExpressionEncoder()
12+
implicit def gtLineEncoder: Encoder[Line] = ExpressionEncoder()
13+
implicit def gtMultiLineEncoder: Encoder[MultiLine] = ExpressionEncoder()
14+
implicit def gtPolygonEncoder: Encoder[Polygon] = ExpressionEncoder()
15+
implicit def gtMultiPolygonEncoder: Encoder[MultiPolygon] = ExpressionEncoder()
16+
17+
implicit def gtFeatureEncoder[G <: Geometry, D](implicit ev1: Encoder[G], ev2: Encoder[D]): Encoder[Feature[G, D]] = Encoders.kryo[Feature[G, D]]
18+
19+
implicit def gtVectorTileEncoder: Encoder[VectorTile] = Encoders.kryo[VectorTile]
20+
//implicit def gtLayerEncoder: Encoder[Layer] = Encoders.javaSerialization[Layer]
21+
//implicit def gtStrictLayerEncoder: Encoder[StrictLayer] = Encoders.kryo[StrictLayer]
22+
}

src/main/scala/vectorpipe/functions/osm/package.scala renamed to core/src/main/scala/vectorpipe/functions/osm/package.scala

File renamed without changes.
File renamed without changes.

src/main/scala/vectorpipe/internal/package.scala renamed to core/src/main/scala/vectorpipe/internal/package.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ package object internal {
182182

183183
// when an element has been deleted, it doesn't include any tags; use a window function to retrieve the last tags
184184
// present and use those
185-
history
185+
frame
186186
.where('type === "relation")
187187
.repartition('id)
188188
.select(
File renamed without changes.

0 commit comments

Comments
 (0)