wellknown
- convert WKT to GeoJSON and vice versa.
Inspiration partly comes from Python's geomet/geomet - and the name from Javascript's wellknown (it's a good name).
There's a family of functions that make it easy to go from familiar R objects like lists and data.frames to WKT, including:
point()
- make a point, e.g., POINT (-116 45)
multipoint()
- make a multipoint, e.g., MULTIPOINT ((100 3), (101 2))
linestring()
- make a linestring, e.g., LINESTRING (100 0, 101 1)
polygon()
- make a polygon, e.g., POLYGON ((100 0), (101 0), (101 1), (100 0))
multipolygon()
- make a multipolygon, e.g., MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))
The above currently accept (depending on the fxn) numeric
, list
, and data.frame
(and character
for special case of EMPTY
WKT objects).
geojson2wkt()
and wkt2geojson()
cover a subset of the various formats available:
Point
MultiPoint
Polygon
MultiPolygon
LineString
MultilineString
Geometrycollection
geojson2wkt()
converts any geojson as a list to a WKT string (the same format )
wkt2geojson()
converts any WKT string into geojson as a list. This list format for geojson can be used downstream e.g., in the leaflet
package.
wkt_wkb()
converts WKT to WKB, while wkb_wkt()
converts WKB to WKT
Stable version
install.packages("wellknown")
Dev version
remotes::install_github("ropensci/wellknown") # OR install.packages("wellknown", repos="https://dev.ropensci.org")
library("wellknown")
point <- list(Point = c(116.4, 45.2, 11.1)) geojson2wkt(point) #> [1] "POINT Z(116.4000000000000057 45.2000000000000028 11.0999999999999996)"
mp <- list( MultiPoint = matrix(c(100, 101, 3.14, 3.101, 2.1, 2.18), ncol = 2) ) geojson2wkt(mp) #> [1] "MULTIPOINT ((100.0000000000000000 3.1010000000000000), (101.0000000000000000 2.1000000000000001), (3.1400000000000001 2.1800000000000002))"
st <- list( LineString = matrix(c(0.0, 2.0, 4.0, 5.0, 0.0, 1.0, 2.0, 4.0), ncol = 2) ) geojson2wkt(st, fmt=0) #> [1] "LINESTRING (0 0, 2 1, 4 2, 5 4)"
multist <- list( MultiLineString = list( matrix(c(0, -2, -4, -1, -3, -5), ncol = 2), matrix(c(1.66, 10.9999, 10.9, 0, -31.5, 3.0, 1.1, 0), ncol = 2) ) ) geojson2wkt(multist) #> [1] "MULTILINESTRING ((0.0000000000000000 -1.0000000000000000, -2.0000000000000000 -3.0000000000000000, -4.0000000000000000 -5.0000000000000000), (1.6599999999999999 -31.5000000000000000, 10.9999000000000002 3.0000000000000000, 10.9000000000000004 1.1000000000000001, 0.0000000000000000 0.0000000000000000))"
poly <- list( Polygon = list( matrix(c(100.001, 101.1, 101.001, 100.001, 0.001, 0.001, 1.001, 0.001), ncol = 2), matrix(c(100.201, 100.801, 100.801, 100.201, 0.201, 0.201, 0.801, 0.201), ncol = 2) ) ) geojson2wkt(poly) #> [1] "POLYGON ((100.0010000000000048 0.0010000000000000, 101.0999999999999943 0.0010000000000000, 101.0010000000000048 1.0009999999999999, 100.0010000000000048 0.0010000000000000), (100.2009999999999934 0.2010000000000000, 100.8010000000000019 0.2010000000000000, 100.8010000000000019 0.8010000000000000, 100.2009999999999934 0.2010000000000000))"
mpoly <- list( MultiPolygon = list( list( matrix(c(100, 101, 101, 100, 0.001, 0.001, 1.001, 0.001), ncol = 2), matrix(c(100.2, 100.8, 100.8, 100.2, 0.2, 0.2, 0.8, 0.2), ncol = 2) ), list( matrix(c(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 1.0), ncol = 3), matrix(c(9.0, 10.0, 11.0, 12.0, 1.0, 2.0, 3.0, 4.0, 9.0), ncol = 3) ) ) ) geojson2wkt(mpoly, fmt=1) #> [1] "MULTIPOLYGON Z(((100.000 0.001 0.000, 101.000 0.001 0.000, 101.000 1.001 0.000, 100.000 0.001 0.000), (100.2 0.2 0.0, 100.8 0.2 0.0, 100.8 0.8 0.0, 100.2 0.2 0.0)), ((1.0 4.0 7.0, 2.0 5.0 8.0, 3.0 6.0 1.0), (9.0 12.0 3.0, 10.0 1.0 4.0, 11.0 2.0 9.0)))"
gmcoll <- list( GeometryCollection = list( list(type = 'Point', coordinates = c(0.0, 1.0)), list(type = 'LineString', coordinates = matrix(c(0.0, 2.0, 4.0, 5.0, 0.0, 1.0, 2.0, 4.0), ncol = 2)), list(type = 'Polygon', coordinates = list( matrix(c(100.001, 101.1, 101.001, 100.001, 0.001, 0.001, 1.001, 0.001), ncol = 2), matrix(c(100.201, 100.801, 100.801, 100.201, 0.201, 0.201, 0.801, 0.201), ncol = 2) )) ) ) geojson2wkt(gmcoll, fmt=0) #> [1] "GEOMETRYCOLLECTION (POINT (0 1), LINESTRING (0 0, 2 1, 4 2, 5 4), POLYGON ((100.001 0.001, 101.100 0.001, 101.001 1.001, 100.001 0.001), (100.201 0.201, 100.801 0.201, 100.801 0.801, 100.201 0.201)))"
You can convert directly from an object of class json
, which is output from jsonlite::toJSON()
.
library("jsonlite") (json <- toJSON(list(Point = c(-105, 39)), auto_unbox = TRUE)) #> {"Point":[-105,39]}
geojson2wkt(json) #> [1] "POINT (-105 39)"
And you can convert from a geojson character string:
str <- '{"type":"LineString","coordinates":[[0,0,10],[2,1,20],[4,2,30],[5,4,40]]}' geojson2wkt(str) #> [1] "LINESTRING Z(0 0 10, 2 1 20, 4 2 30, 5 4 40)"
As a Feature
str <- "POINT (-116.4000000000000057 45.2000000000000028)" wkt2geojson(str) #> $type #> [1] "Feature" #> #> $geometry #> $geometry$type #> [1] "Point" #> #> $geometry$coordinates #> [1] -116.4 45.2 #> ...
Not Feature
wkt2geojson(str, feature=FALSE) #> $type #> [1] "Point" #> #> $coordinates #> [1] -116.4 45.2 #> #> attr(,"class") #> [1] "geojson"
str <- 'MULTIPOINT ((100.000 3.101), (101.000 2.100), (3.140 2.180))' wkt2geojson(str, feature=FALSE) #> $type #> [1] "MultiPoint" #> #> $coordinates #> [,1] [,2] #> [1,] 100.00 3.101 #> [2,] 101.00 2.100 #> [3,] 3.14 2.180 #> #> attr(,"class") ...
str <- "POLYGON ((100 0.1, 101.1 0.3, 101 0.5, 100 0.1), (103.2 0.2, 104.8 0.2, 100.8 0.8, 103.2 0.2))" wkt2geojson(str, feature=FALSE) #> $type #> [1] "Polygon" #> #> $coordinates #> $coordinates[[1]] #> [,1] [,2] #> [1,] 100.0 0.1 #> [2,] 101.1 0.3 #> [3,] 101.0 0.5 #> [4,] 100.0 0.1 ...
str <- "MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 45 20, 30 5, 10 10, 10 30, 20 35), (30 20, 20 25, 20 15, 30 20)))" wkt2geojson(str, feature=FALSE) #> $type #> [1] "MultiPolygon" #> #> $coordinates #> $coordinates[[1]] #> $coordinates[[1]][[1]] #> [,1] [,2] #> [1,] 40 40 #> [2,] 20 45 #> [3,] 45 30 ...
wkt2geojson("LINESTRING (0 -1, -2 -3, -4 5)", feature=FALSE) #> $type #> [1] "LineString" #> #> $coordinates #> [,1] [,2] #> [1,] 0 -1 #> [2,] -2 -3 #> [3,] -4 5 #> #> attr(,"class") ...
lint("POINT (1 2)") #> [1] TRUE lint("LINESTRING EMPTY") #> [1] TRUE lint("MULTIPOINT ((1 2), (3 4), (-10 100))") #> [1] TRUE lint("POLYGON((20.3 28.6, 20.3 19.6, 8.5 19.6, 8.5 28.6, 20.3 28.6))") #> [1] TRUE lint("MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))") #> [1] TRUE lint("POINT (1 2 3 4 5)") #> [1] FALSE lint("LINESTRING (100)") #> [1] FALSE lint("MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)), ((15 5, a b, 10 20, 5 10, 15 5)))") #> [1] FALSE
WKT to WKB
## point wkt_wkb("POINT (-116.4 45.2)") #> [1] 01 01 00 00 00 9a 99 99 99 99 19 5d c0 9a 99 99 99 99 99 46 40 ## polygon wkt_wkb("POLYGON ((100.0 0.0, 101.1 0.0, 101.0 1.0, 100.0 0.0))") #> [1] 01 03 00 00 00 01 00 00 00 04 00 00 00 00 00 00 00 00 00 59 40 00 00 00 00 #> [26] 00 00 00 00 66 66 66 66 66 46 59 40 00 00 00 00 00 00 00 00 00 00 00 00 00 #> [51] 40 59 40 00 00 00 00 00 00 f0 3f 00 00 00 00 00 00 59 40 00 00 00 00 00 00 #> [76] 00 00
WKB to WKT
## point (x <- wkt_wkb("POINT (-116.4 45.2)")) #> [1] 01 01 00 00 00 9a 99 99 99 99 19 5d c0 9a 99 99 99 99 99 46 40 wkb_wkt(x) #> [1] "POINT (-116.4 45.2)" ## polygon (x <- wkt_wkb("POLYGON ((100.0 0.0, 101.1 0.0, 101.0 1.0, 100.0 0.0))")) #> [1] 01 03 00 00 00 01 00 00 00 04 00 00 00 00 00 00 00 00 00 59 40 00 00 00 00 #> [26] 00 00 00 00 66 66 66 66 66 46 59 40 00 00 00 00 00 00 00 00 00 00 00 00 00 #> [51] 40 59 40 00 00 00 00 00 00 f0 3f 00 00 00 00 00 00 59 40 00 00 00 00 00 00 #> [76] 00 00 wkb_wkt(x) #> [1] "POLYGON ((100 0, 101.1 0, 101 1, 100 0))"
A bounding box is a very simple concept: a representation of the smallest area in which all the points in a dataset lie. In WKT, bounding boxes look like:
POLYGON((10 14,10 16,12 16,12 14,10 14))
Sometimes you've got WKT data like this - a Polygon, a LineString, whatever - and you want a bounding box in a format R can understand. The answer is wkt_bounding
, which takes a vector of valid WKT objects and produces a data.frame or matrix of R representations, whichever you'd prefer:
wkt <- c("POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))", "LINESTRING (30 10, 10 90, 40 40)") wkt_bounding(wkt) #> min_x min_y max_x max_y #> 1 10 10 40 40 #> 2 10 10 40 90
Alternately you might want to go in the other direction and turn R bounding boxes into WKT objects. You can do that with, appropriately, bounding_wkt
:
bounding_wkt(min_x = 10, min_y = 10, max_x = 40, max_y = 40) #> [1] "POLYGON((10 10,10 40,40 40,40 10,10 10))"
This accepts either a series of vectors, one for each min or max value, or a list of length-4 vectors. Either way, it produces a nice WKT representation of the R data you give it.
validate_wkt
takes a vector of WKT objects and spits out a data.frame containing whether each object is valid, and any comments the parser has in the case that it isn't:
wkt <- c("POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))", "ARGHLEFLARFDFG", "LINESTRING (30 10, 10 90, 40 some string)") validate_wkt(wkt) #> is_valid #> 1 FALSE #> 2 FALSE #> 3 FALSE #> comments #> 1 The WKT object has a different orientation from the default. Use ?wkt_correct to fix. #> 2 Object could not be recognised as a supported WKT type #> 3 bad lexical cast: source type value could not be interpreted as target at 'some' in 'linestring (30 10, 10 90, 40 some string)'
With this you can check and clean your data before you rely on it and watch all your code fall down in a heap.
WKT POLYGONs are often used to store latitude and longitude coordinates - and you can use wkt_coords
to get them:
wkt_coords(("POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))")) #> object ring lng lat #> 1 1 outer 30 10 #> 2 1 outer 40 40 #> 3 1 outer 20 40 #> 4 1 outer 10 20 #> 5 1 outer 30 10
The result of a wkt_coords
call is a data.frame of four columns - object
, identifying which of the input WKT objects the row refers to, ring
referring to the layer in that object, and then lat
and lng
.
Extracting centroids is also useful, and can be performed with wkt_centroid
. Again, it's entirely vectorised and produces a data.frame:
wkt_centroid(("POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))")) #> lng lat #> 1 25.45455 26.9697
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.