ropensci / geojsonio

Convert many data formats to & from GeoJSON & TopoJSON
https://docs.ropensci.org/geojsonio
Other
150 stars 59 forks source link

geojson_json to return a single Feature, rather than FeatureCollection #119

Closed SymbolixAU closed 7 years ago

SymbolixAU commented 7 years ago

When converting a single row of an sf object to geoJSON, is it possible to return a single Feature, rather than a FeatureCollection ?

Example

Taking the data supplied by sf, if you convert a single row it returns a FeatureCollection

library(sf)
sf <- st_read(system.file("shape/nc.shp", package="sf"))

geojsonio::geojson_json(sf[1, ], pretty = T)

# {
#   "type": "FeatureCollection",
#   "features": [
#     {
#       "type": "Feature",
#       "properties": {
#         "AREA": 0.114,
#         "PERIMETER": 1.442,
#         ... etc ...
#       },
#       "geometry": {
#         "type": "MultiPolygon",
#         "coordinates": [ [ [ ] ] ]

Would it be possible to return this just as a Feature ?

  #     {
  #       "type": "Feature",
  #       "properties": {
  #         "AREA": 0.114,
  #         "PERIMETER": 1.442,
  #         ... etc ...
  #       },
  #       "geometry": {
  #         "type": "MultiPolygon",
  #         "coordinates": [ [ [ ] ] ]

The use-case I'm thinking for this is storage in a GeoDatabase (MongoDB), where you want a document per feature, rather than inserting all the objects as an entire FeatureCollection.

sckott commented 7 years ago

Thanks for this @SymbolixAU

You could do that afterwards with jqr, e.g.,

library(geojsonio)
library(sf)
library(jqr)
p1 <- rbind(c(0,0), c(1,0), c(3,2), c(2,4), c(1,4), c(0,0))
p2 <- rbind(c(5,5), c(5,6), c(4,5), c(5,5))
poly_sfc <- st_sfc(st_polygon(list(p1)), st_polygon(list(p2)))
poly_sf <- st_sf(foo = c("a", "b"), bar = 1:2, poly_sfc)
(x <- geojson_json(poly_sf))
#> {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"foo":"a","bar":1},"geometry":{"type":"Polygon","coordinates":[[[0,0],[1,0],[3,2],[2,4],[1,4],[0,0]]]}},{"type":"Feature","properties":{"foo":"b","bar":2},"geometry":{"type":"Polygon","coordinates":[[[5,5],[5,6],[4,5],[5,5]]]}}]}
unclass(jq(unclass(x), ".features[]"))
#>  [1] "{\"type\":\"Feature\",\"properties\":{\"foo\":\"a\",\"bar\":1},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[0,0],[1,0],[3,2],[2,4],[1,4],[0,0]]]}}"
#>  [2] "{\"type\":\"Feature\",\"properties\":{\"foo\":\"b\",\"bar\":2},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[5,5],[5,6],[4,5],[5,5]]]}}"

But, that's of course a solution that doesn't really get at your question within this pkg. We may be able to allow this in geojson_json/geojson_list - though need to think about how it would work.

With geojson_list it's straightforward to index to the array of features output$features, but not so easy with geojson_json output

cc @ateucher

sckott commented 7 years ago

@SymbolixAU okay, reinstall devtools::install_github("ropensci/geojsonio@atomize")

see geojson_atomize and it's examples

i thought it might be easier to make a new fxn rather than change behavior in the two work horse fxns geojson_json/geojson_list

right now the fxn works with output of those two fxns just listed.

thoughts?

the fxn name may need to change as atomize only fits the first two egs in the list above

SymbolixAU commented 7 years ago

Thanks for looking at this; and for adding it to your next milestone! I think this is a good start.

However, I'm wondering if it's erroneous to class the output as json, given:

library(sf)
nc <- st_read(system.file("shape/nc.shp", package="sf"))
geo <- geojson_json(nc)
atom <- geojson_atomize(geo)
class(atom)
# "json"
jsonlite::validate(atom)
# [1] FALSE
# error...

Maybe the returned value(s) should be a vector of JSON objects, rather than collapsed down into one long char?

sckott commented 7 years ago

try again

SymbolixAU commented 7 years ago

yep - that's got it working

SymbolixAU commented 7 years ago

I think it might be worth allowing the user to specify if they want to combine the tmp object through an argument in the function - thoughts?

This goes back to my original use-case of storing the data in a database, where you may want to insert geometries/features as separate documents.

sckott commented 7 years ago

good idea. Do you ever want them as separate JSON objects? or always as one JSON object?

SymbolixAU commented 7 years ago

I think I would get more use out of separate JSON objects, primarily because I can then work with each geometry/feature independently, both for plotting and storing.

For example, this workflow

x <- geo

stripjqr <- function(x) gsub('\\"', "", unclass(x))

type <- stripjqr(jqr::jq(unclass(x), ".type"))
keys <- stripjqr(jqr::jq(unclass(x), "keys[]"))
if (type %in% c('Point', 'MultiPoint', 
                'Polygon', 'MultiPolygon', 
                'LineString', 'MultiLineString')) {
  tmp <- jqr::jq(unclass(x), '{ "type": "Feature", "geometry": . }')
} else {
  if ("features" %in% keys) {
    tmp <- jqr::jq(unclass(x), ".features[]")
  }
  if ("geometries" %in% keys) {
    tmp <- jqr::jq(unclass(x), ".geometries[]")
  }
}

library(mongolite)
library(symbolix.utils)
mong <- ConnectToMongo(db = "test", collection = "geotest", usr = "db_user")
# mong$drop()
mong$insert(tmp)
# List of 6
# $ nInserted  : int 100
# $ nMatched   : int 0
# $ nModified  : int 0
# $ nRemoved   : int 0
# $ nUpserted  : int 0
# $ writeErrors: list()

inserts 100 features into the database. This then allows me to geo-index the geometries and do geometry calculations on-the-fly on the server. (I wrote a recent blog post giving an example of what I'm doing, if you're intersted: https://www.symbolix.com.au/blog-main/2017-3)

sckott commented 7 years ago

thanks. i think it makes sense to provide a parameter to toggle this behavior - i think it makes sense to have default of one single JSON valid output since that's what this pkg does - jqr doesn't necessarily do that, but i think we should be consistent with behavior that already exists in this pkg

p.s. note that we don't add the properties element, which is needed often for other downstream things (e.g., the leaflet R package and http://geojson.io)

SymbolixAU commented 7 years ago

Yeah happy with that. And thanks for introducing me to jqr too, it's made my life a whole lot easier https://github.com/SymbolixAU/googleway/blob/master/R/results_access.R#L110 !

p.s. that's Ok, I can live without the properties element

geojson_txt <- '{
    "type" : "Feature",
    "geometry" : {
    "type" : "Polygon", "coordinates" : [ 
      [
        [144.88, -37.85],
        [145.02, -37.85],
        [145.02, -37.80],
        [144.88, -37.80],
        [144.88, -37.85]
      ]
    ]
  }
}'

## geojson is in dev
devtools::install_git("SymbolixAU/googleway")

mapKey <- symbolix.utils::mapKey()

google_map(key = mapKey) %>% 
  add_geojson(geojson = geojson_txt)  

screen shot 2017-09-15 at 7 53 10 am

sckott commented 7 years ago

great, glad you like jqr - glad we were able to wrap it up nicely in R (same person did the heavy lifting that made mongolite 😸 )

SymbolixAU commented 7 years ago

yeah, I'm well aware/greatful to Jeroen :) - I use his packages a lot !

github-actions[bot] commented 2 years ago

This issue has been automatically locked. If you believe you have found a related problem, please file a new issue (with a reprex: https://reprex.tidyverse.org) and link to this issue.