├── .Rbuildignore ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ └── issue_template.md └── workflows │ ├── issue.yml │ ├── stale-actions.yml │ └── tic.yml ├── .gitignore ├── DESCRIPTION ├── Dockerfile ├── LICENSE ├── NAMESPACE ├── NEWS.md ├── R ├── RcppExports.R ├── geojsonR.R └── utils.R ├── README.md ├── inst ├── CITATION └── COPYRIGHTS ├── man ├── Dump_From_GeoJson.Rd ├── FROM_GeoJson.Rd ├── FROM_GeoJson_Schema.Rd ├── Features_2Collection.Rd ├── TO_GeoJson.Rd ├── merge_files.Rd ├── save_R_list_Features_2_FeatureCollection.Rd └── shiny_from_JSON.Rd ├── src ├── FROM_geojson.cpp ├── Makevars ├── Makevars.win ├── RcppExports.cpp ├── TO_geojson.cpp ├── init.c ├── json11.cpp └── json11.h ├── tests ├── testthat.R └── testthat │ ├── .Rhistory │ ├── file_data │ ├── Feature.geojson │ ├── Schema_data.geojson │ └── feature_multiple_files │ │ ├── Feature1.geojson │ │ ├── Feature2.geojson │ │ ├── Feature3.geojson │ │ ├── Feature4.geojson │ │ └── Feature5.geojson │ ├── merge_folder │ ├── example1.json │ └── example2.json │ └── test-geojson.R ├── tic.R └── vignettes ├── geocoding_nominatim.png ├── geocoding_nominatim_reverse.png ├── geojson_geocoding.png └── the_geojsonR_package.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^Dockerfile$ 2 | ^.git$ 3 | ^.github$ 4 | ^\.ccache$ 5 | ^\.github$ 6 | ^tic\.R$ 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | # For more info see: https://docs.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser 2 | 3 | blank_issues_enabled: true 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report or feature request 3 | about: Describe a bug you've encountered or make a case for a new feature 4 | --- 5 | 6 | Please briefly describe your problem and what output you expect. If you have a question, you also have the option of (but I'm flexible if it's not too complicated) 7 | 8 | Please include a minimal reproducible example 9 | 10 | Please give a brief description of the problem 11 | 12 | Please add your Operating System (e.g., Windows10, Macintosh, Linux) and the R version that you use (e.g., 4.0.2) 13 | -------------------------------------------------------------------------------- /.github/workflows/issue.yml: -------------------------------------------------------------------------------- 1 | # For more info see: https://github.com/Renato66/auto-label 2 | # for the 'secrets.GITHUB_TOKEN' see: https://docs.github.com/en/actions/reference/authentication-in-a-workflow#about-the-github_token-secret 3 | 4 | name: Labeling new issue 5 | on: 6 | issues: 7 | types: ['opened'] 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: Renato66/auto-label@v2 13 | with: 14 | repo-token: ${{ secrets.GITHUB_TOKEN }} 15 | ignore-comments: true 16 | labels-synonyms: '{"bug":["error","need fix","not working"],"enhancement":["upgrade"],"question":["help","how can i"]}' 17 | labels-not-allowed: '["documentation","duplicate","good first issue","help wanted","invalid"]' 18 | default-labels: '["triage"]' 19 | -------------------------------------------------------------------------------- /.github/workflows/stale-actions.yml: -------------------------------------------------------------------------------- 1 | # for the 'secrets.GITHUB_TOKEN' see: https://docs.github.com/en/actions/reference/authentication-in-a-workflow#about-the-github_token-secret 2 | 3 | name: "Mark or close stale issues and PRs" 4 | 5 | on: 6 | schedule: 7 | - cron: "00 * * * *" 8 | 9 | jobs: 10 | stale: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/stale@v3 14 | with: 15 | repo-token: ${{ secrets.GITHUB_TOKEN }} 16 | days-before-stale: 12 17 | days-before-close: 7 18 | stale-issue-message: "This is Robo-lampros because the Human-lampros is lazy. This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 7 days if no further activity occurs. Feel free to re-open a closed issue and the Human-lampros will respond." 19 | stale-pr-message: "This is Robo-lampros because the Human-lampros is lazy. This PR has been automatically marked as stale because it has not had recent activity. It will be closed after 7 days if no further activity occurs." 20 | close-issue-message: "This issue was automatically closed because of being stale. Feel free to re-open a closed issue and the Human-lampros will respond." 21 | close-pr-message: "This PR was automatically closed because of being stale." 22 | stale-pr-label: "stale" 23 | stale-issue-label: "stale" 24 | exempt-issue-labels: "bug,enhancement,pinned,security,pending,work_in_progress" 25 | exempt-pr-labels: "bug,enhancement,pinned,security,pending,work_in_progress" 26 | -------------------------------------------------------------------------------- /.github/workflows/tic.yml: -------------------------------------------------------------------------------- 1 | ## tic GitHub Actions template: linux-macos-windows-deploy 2 | ## revision date: 2020-12-11 3 | on: 4 | workflow_dispatch: 5 | push: 6 | pull_request: 7 | # for now, CRON jobs only run on the default branch of the repo (i.e. usually on master) 8 | schedule: 9 | # * is a special character in YAML so you have to quote this string 10 | - cron: "0 4 * * *" 11 | 12 | name: tic 13 | 14 | jobs: 15 | all: 16 | runs-on: ${{ matrix.config.os }} 17 | 18 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | config: 24 | # use a different tic template type if you do not want to build on all listed platforms 25 | - { os: windows-latest, r: "release" } 26 | - { os: macOS-latest, r: "release", pkgdown: "true", latex: "true" } 27 | - { os: ubuntu-latest, r: "devel" } 28 | - { os: ubuntu-latest, r: "release" } 29 | 30 | env: 31 | # otherwise remotes::fun() errors cause the build to fail. Example: Unavailability of binaries 32 | R_REMOTES_NO_ERRORS_FROM_WARNINGS: true 33 | CRAN: ${{ matrix.config.cran }} 34 | # make sure to run `tic::use_ghactions_deploy()` to set up deployment 35 | TIC_DEPLOY_KEY: ${{ secrets.TIC_DEPLOY_KEY }} 36 | # prevent rgl issues because no X11 display is available 37 | RGL_USE_NULL: true 38 | # if you use bookdown or blogdown, replace "PKGDOWN" by the respective 39 | # capitalized term. This also might need to be done in tic.R 40 | BUILD_PKGDOWN: ${{ matrix.config.pkgdown }} 41 | # macOS >= 10.15.4 linking 42 | SDKROOT: /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk 43 | # use GITHUB_TOKEN from GitHub to workaround rate limits in {remotes} 44 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 45 | 46 | steps: 47 | - uses: actions/checkout@v3 48 | 49 | - uses: r-lib/actions/setup-r@v2 50 | with: 51 | r-version: ${{ matrix.config.r }} 52 | Ncpus: 4 53 | 54 | # LaTeX. Installation time: 55 | # Linux: ~ 1 min 56 | # macOS: ~ 1 min 30s 57 | # Windows: never finishes 58 | - uses: r-lib/actions/setup-tinytex@v2 59 | if: matrix.config.latex == 'true' 60 | 61 | - uses: r-lib/actions/setup-pandoc@v2 62 | 63 | # set date/week for use in cache creation 64 | # https://github.community/t5/GitHub-Actions/How-to-set-and-access-a-Workflow-variable/m-p/42970 65 | # - cache R packages daily 66 | - name: "[Cache] Prepare daily timestamp for cache" 67 | if: runner.os != 'Windows' 68 | id: date 69 | run: echo "::set-output name=date::$(date '+%d-%m')" 70 | 71 | - name: "[Cache] Cache R packages" 72 | if: runner.os != 'Windows' 73 | uses: pat-s/always-upload-cache@v2.1.3 74 | with: 75 | path: ${{ env.R_LIBS_USER }} 76 | key: ${{ runner.os }}-r-${{ matrix.config.r }}-${{steps.date.outputs.date}} 77 | restore-keys: ${{ runner.os }}-r-${{ matrix.config.r }}-${{steps.date.outputs.date}} 78 | 79 | # for some strange Windows reason this step and the next one need to be decoupled 80 | - name: "[Stage] Prepare" 81 | run: | 82 | Rscript -e "if (!requireNamespace('remotes')) install.packages('remotes', type = 'source')" 83 | Rscript -e "if (getRversion() < '3.2' && !requireNamespace('curl')) install.packages('curl', type = 'source')" 84 | 85 | - name: "[Stage] [Linux] Install curl and libgit2" 86 | if: runner.os == 'Linux' 87 | run: sudo apt install libcurl4-openssl-dev libgit2-dev 88 | 89 | - name: "[Stage] [macOS] Install libgit2" 90 | if: runner.os == 'macOS' 91 | run: brew install libgit2 92 | 93 | - name: "[Stage] [macOS] Install system libs for pkgdown" 94 | if: runner.os == 'macOS' && matrix.config.pkgdown != '' 95 | run: brew install harfbuzz fribidi 96 | 97 | - name: "[Stage] [Linux] Install system libs for pkgdown" 98 | if: runner.os == 'Linux' && matrix.config.pkgdown != '' 99 | run: sudo apt install libharfbuzz-dev libfribidi-dev 100 | 101 | - name: "[Stage] Install" 102 | if: matrix.config.os != 'macOS-latest' || matrix.config.r != 'devel' 103 | run: Rscript -e "remotes::install_github('ropensci/tic')" -e "print(tic::dsl_load())" -e "tic::prepare_all_stages()" -e "tic::before_install()" -e "tic::install()" 104 | 105 | # macOS devel needs its own stage because we need to work with an option to suppress the usage of binaries 106 | - name: "[Stage] Prepare & Install (macOS-devel)" 107 | if: matrix.config.os == 'macOS-latest' && matrix.config.r == 'devel' 108 | run: | 109 | echo -e 'options(Ncpus = 4, pkgType = "source", repos = structure(c(CRAN = "https://cloud.r-project.org/")))' > $HOME/.Rprofile 110 | Rscript -e "remotes::install_github('ropensci/tic')" -e "print(tic::dsl_load())" -e "tic::prepare_all_stages()" -e "tic::before_install()" -e "tic::install()" 111 | 112 | - name: "[Stage] Script" 113 | run: Rscript -e 'tic::script()' 114 | 115 | - name: "[Stage] After Success" 116 | if: matrix.config.os == 'macOS-latest' && matrix.config.r == 'release' 117 | run: Rscript -e "tic::after_success()" 118 | 119 | - name: "[Stage] Upload R CMD check artifacts" 120 | if: failure() 121 | uses: actions/upload-artifact@v4 122 | with: 123 | name: ${{ runner.os }}-r${{ matrix.config.r }}-results 124 | path: check 125 | - name: "[Stage] Before Deploy" 126 | run: | 127 | Rscript -e "tic::before_deploy()" 128 | 129 | - name: "[Stage] Deploy" 130 | run: Rscript -e "tic::deploy()" 131 | 132 | - name: "[Stage] After Deploy" 133 | run: Rscript -e "tic::after_deploy()" 134 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | src/*.o 6 | src/*.so 7 | src/*.dll 8 | docs/ 9 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: geojsonR 2 | Type: Package 3 | Title: A GeoJson Processing Toolkit 4 | Version: 1.1.2 5 | Date: 2025-01-13 6 | Authors@R: c( person(given = "Lampros", family = "Mouselimis", email = "mouselimislampros@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "https://orcid.org/0000-0002-8024-1546")), person("Dropbox", "Inc", role = "cph")) 7 | BugReports: https://github.com/mlampros/geojsonR/issues 8 | URL: https://github.com/mlampros/geojsonR 9 | Description: Includes functions for processing GeoJson objects relying on 'RFC 7946' . The geojson encoding is based on 'json11', a tiny JSON library for 'C++11' . Furthermore, the source code is exported in R through the 'Rcpp' and 'RcppArmadillo' packages. 10 | License: MIT + file LICENSE 11 | Encoding: UTF-8 12 | Copyright: inst/COPYRIGHTS 13 | SystemRequirements: libarmadillo: apt-get install -y libarmadillo-dev (deb) 14 | Depends: 15 | R(>= 3.2.3) 16 | Imports: 17 | Rcpp (>= 0.12.9), 18 | R6 19 | LinkingTo: Rcpp, RcppArmadillo (>= 0.7.6) 20 | Suggests: 21 | testthat, 22 | covr, 23 | knitr, 24 | rmarkdown 25 | VignetteBuilder: knitr 26 | RoxygenNote: 7.3.2 27 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rocker/rstudio:devel 2 | 3 | 4 | LABEL maintainer='Lampros Mouselimis' 5 | 6 | 7 | RUN export DEBIAN_FRONTEND=noninteractive; apt-get -y update && \ 8 | apt-get install -y pandoc pandoc-citeproc git-core libssl-dev libcurl4-openssl-dev && \ 9 | apt-get install -y sudo && \ 10 | apt-get install -y libarmadillo-dev && \ 11 | apt-get install -y libxml2-dev && \ 12 | apt-get install -y libssh2-1-dev && \ 13 | apt-get install -y zlib1g-dev && \ 14 | R -e "install.packages('devtools', dependencies = TRUE, repos = 'https://cloud.r-project.org/')" && \ 15 | R -e "install.packages(c( 'Rcpp', 'R6', 'RcppArmadillo', 'testthat', 'covr', 'knitr', 'rmarkdown', 'remotes' ), repos = 'https://cloud.r-project.org/' )" && \ 16 | R -e "remotes::install_github('mlampros/geojsonR', upgrade = 'never', dependencies = FALSE, repos = 'https://cloud.r-project.org/')" && \ 17 | apt-get autoremove -y && \ 18 | apt-get clean 19 | 20 | 21 | ENV USER rstudio 22 | 23 | 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2017 2 | COPYRIGHT HOLDER: Mouselimis Lampros 3 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(Dump_From_GeoJson) 4 | export(FROM_GeoJson) 5 | export(FROM_GeoJson_Schema) 6 | export(Features_2Collection) 7 | export(TO_GeoJson) 8 | export(merge_files) 9 | export(save_R_list_Features_2_FeatureCollection) 10 | export(shiny_from_JSON) 11 | importFrom(R6,R6Class) 12 | importFrom(Rcpp,evalCpp) 13 | useDynLib(geojsonR, .registration = TRUE) 14 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | 2 | ## geojsonR 1.1.2 3 | 4 | * I fixed broken url's 5 | * I removed the `#define NDEBUG` from the `TO_geojson.cpp` and `FROM_geojson.cpp` files 6 | * I added the `#include ` to the `json11.cpp` file 7 | * I removed the `CXX_STD = CXX11` flag from the `Makevars` files 8 | 9 | 10 | ## geojsonR 1.1.1 11 | 12 | * I modified the *json11.cpp* file by excluding the *using std::move* from the top of the file and converting all *move* to *std::move* to remove the significant warning *"warning: unqualified call to 'std::move' [-Wunqualified-std-cast-call]"* 13 | 14 | 15 | ## geojsonR 1.1.0 16 | 17 | * I've added the *CITATION* file in the *inst* directory listing all papers and software used in the *geojsonR* package 18 | * I've removed *Lazydata* from the DESCRIPTION file 19 | 20 | 21 | ## geojsonR 1.0.9 22 | 23 | * I've adjusted the tolerance value in the test-case of the *save_R_list_Features_2_FeatureCollection* function (file 'test-geojson.R') because it threw an error in 2 operating systems. I also replaced the invalid url's in the vignette. 24 | 25 | 26 | ## geojsonR 1.0.8 27 | 28 | * I've added the *save_R_list_Features_2_FeatureCollection* function and I've modified the *typeof_item* method of the *GeoJson_Collections* C++ class to accept also input objects of type integer. 29 | 30 | 31 | ## geojsonR 1.0.7 32 | 33 | * I've added the *write_path* and *verbose* parameters to the *Features_2Collection* function 34 | * I've added error handling in the *parse_geojson_string* and *parse_geojson_objects* methods of the *From_GeoJson_geometries* C++ class to account for cases where the output of the *json11::Json::parse* function is a NULL object ( I've included also a test case for demonstration ) 35 | 36 | 37 | ## geojsonR 1.0.6 38 | 39 | I fixed minor typos in the .cpp files 40 | 41 | 42 | ## geojsonR 1.0.5 43 | 44 | I modified the R functions which made a connection to Url's (previously the connection was not closed in an appropriate way producing that way a warning) 45 | 46 | 47 | ## geojsonR 1.0.4 48 | 49 | I modified the *Makevars* files to allow *OpenMP* usage. The *geojsonR* package is not parallelized, however the *Armadillo* library uses *OpenMP* internally to improve the execuction time of the functions. 50 | 51 | 52 | ## geojsonR 1.0.3 53 | 54 | I added the *merge_files* function. 55 | 56 | 57 | ## geojsonR 1.0.2 58 | 59 | I added the *FROM_GeoJson_Schema* function. This function is appropriate when the property-names do not match exactly the *RFC 7946* specification ( for instance if the *geometry-object-name* appears as *location*, as is the case sometimes in mongodb queries ). This way one can avoid unnecessary errors when reading *geojson* files/strings. 60 | 61 | 62 | ## geojsonR 1.0.1 63 | 64 | I added a Vignette and corrected mistakes of the examples in the documentation 65 | 66 | 67 | ## geojsonR 1.0.0 68 | 69 | -------------------------------------------------------------------------------- /R/RcppExports.R: -------------------------------------------------------------------------------- 1 | # Generated by using Rcpp::compileAttributes() -> do not edit by hand 2 | # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 3 | 4 | export_From_geojson <- function(input_file, flatten_coords = FALSE, average_coordinates = FALSE, to_list = FALSE) { 5 | .Call(`_geojsonR_export_From_geojson`, input_file, flatten_coords, average_coordinates, to_list) 6 | } 7 | 8 | export_From_JSON <- function(input_file) { 9 | .Call(`_geojsonR_export_From_JSON`, input_file) 10 | } 11 | 12 | dump_geojson <- function(input_data) { 13 | .Call(`_geojsonR_dump_geojson`, input_data) 14 | } 15 | 16 | Features_TO_Collection <- function(feat_files_lst, bbox_vec, verbose = FALSE) { 17 | .Call(`_geojsonR_Features_TO_Collection`, feat_files_lst, bbox_vec, verbose) 18 | } 19 | 20 | export_From_geojson_schema <- function(input_file, GEOMETRY_OBJECT_NAME = "", average_coordinates = FALSE, to_list = FALSE) { 21 | .Call(`_geojsonR_export_From_geojson_schema`, input_file, GEOMETRY_OBJECT_NAME, average_coordinates, to_list) 22 | } 23 | 24 | list_files <- function(path, full_path = TRUE) { 25 | .Call(`_geojsonR_list_files`, path, full_path) 26 | } 27 | 28 | merge_json <- function(input_folder, output_file, concat_delimiter = "\n", verbose = FALSE) { 29 | invisible(.Call(`_geojsonR_merge_json`, input_folder, output_file, concat_delimiter, verbose)) 30 | } 31 | 32 | DATA_TYPE <- function(sublist) { 33 | .Call(`_geojsonR_DATA_TYPE`, sublist) 34 | } 35 | 36 | inner_coords <- function(geom_lst, z, polygon_interior = FALSE) { 37 | .Call(`_geojsonR_inner_coords`, geom_lst, z, polygon_interior) 38 | } 39 | 40 | Polygon_with_interior_rings <- function(geom_lst, i, verbose) { 41 | .Call(`_geojsonR_Polygon_with_interior_rings`, geom_lst, i, verbose) 42 | } 43 | 44 | SAVE_R_list_Features_2_FeatureCollection <- function(x, path_to_file = "", verbose = FALSE) { 45 | .Call(`_geojsonR_SAVE_R_list_Features_2_FeatureCollection`, x, path_to_file, verbose) 46 | } 47 | 48 | export_To_GeoJson <- function(geometry_object, data_POINTS, data_ARRAYS, data_ARRAY_ARRAYS, data_POLYGON_ARRAYS, stringify = FALSE) { 49 | .Call(`_geojsonR_export_To_GeoJson`, geometry_object, data_POINTS, data_ARRAYS, data_ARRAY_ARRAYS, data_POLYGON_ARRAYS, stringify) 50 | } 51 | 52 | Geom_Collection <- function(geometry_object_names, geometry_objects, stringify = FALSE) { 53 | .Call(`_geojsonR_Geom_Collection`, geometry_object_names, geometry_objects, stringify) 54 | } 55 | 56 | Feature_Obj <- function(geometry_object_names, geometry_objects, stringify = FALSE) { 57 | .Call(`_geojsonR_Feature_Obj`, geometry_object_names, geometry_objects, stringify) 58 | } 59 | 60 | Feature_collection_Obj <- function(geometry_object_names, geometry_objects, stringify = FALSE) { 61 | .Call(`_geojsonR_Feature_collection_Obj`, geometry_object_names, geometry_objects, stringify) 62 | } 63 | 64 | -------------------------------------------------------------------------------- /R/geojsonR.R: -------------------------------------------------------------------------------- 1 | #' @useDynLib geojsonR, .registration = TRUE 2 | #' @importFrom Rcpp evalCpp 3 | NULL -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | #' reads GeoJson data 4 | #' 5 | #' @param url_file_string a string specifying the input path to a file OR a geojson object (in form of a character string) OR a valid url (beginning with 'http..') pointing to a geojson object 6 | #' @param Flatten_Coords either TRUE or FALSE. If TRUE then the properties member of the geojson file will be omitted during parsing. 7 | #' @param Average_Coordinates either TRUE or FALSE. If TRUE then additionally a geojson-dump and the average latitude and longitude of the geometry object will be returned. 8 | #' @param To_List either TRUE or FALSE. If TRUE then the \emph{coordinates} of the geometry object will be returned in form of a list, otherwise in form of a numeric matrix. 9 | #' @return a (nested) list 10 | #' @details 11 | #' The \emph{FROM_GeoJson} function is based on the 'RFC 7946' specification. Thus, geojson files/strings which include property-names other than the 'RFC 7946' specifies will return an error. To avoid errors of 12 | #' that kind a user should take advantage of the \emph{FROM_GeoJson_Schema} function, which is not as strict concerning the property names. 13 | #' @export 14 | #' @examples 15 | #' 16 | #' \dontrun{ 17 | #' 18 | #' library(geojsonR) 19 | #' 20 | #' 21 | #' # INPUT IS A FILE 22 | #' 23 | #' res = FROM_GeoJson(url_file_string = "/myfolder/feature_collection.geojson") 24 | #' 25 | #' 26 | #' # INPUT IS A GEOJSON (character string) 27 | #' 28 | #' tmp_str = '{ "type": "MultiPolygon", "coordinates": [ 29 | #' [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]], 30 | #' [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]], 31 | #' [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]] 32 | #' ] 33 | #' }' 34 | #' 35 | #' res = FROM_GeoJson(url_file_string = tmp_str) 36 | #' 37 | #' 38 | #' # INPUT IS A URL 39 | #' 40 | #' res = FROM_GeoJson(url_file_string = "http://www.EXAMPLE_web_page.geojson") 41 | #' } 42 | #' 43 | 44 | FROM_GeoJson = function(url_file_string, Flatten_Coords = FALSE, Average_Coordinates = FALSE, To_List = FALSE) { 45 | 46 | if (!inherits(url_file_string, 'character') && length(url_file_string) != 1) { stop("the 'url_file_string' parameter should be of type character string", call. = F) } 47 | if (!inherits(Flatten_Coords, "logical")) { stop("the 'Flatten_Coords' parameter should be of type boolean", call. = F) } 48 | if (!inherits(Average_Coordinates, "logical")) { stop("the 'Average_Coordinates' parameter should be of type boolean", call. = F) } 49 | if (!inherits(To_List, "logical")) { stop("the 'To_List' parameter should be of type boolean", call. = F) } 50 | 51 | if (substring(url_file_string, 1, 4) == "http") { # only url-addresses which start with 'http' will be considered as valid 52 | 53 | con = url(url_file_string, method = "libcurl") # test url-output with : 'https://raw.githubusercontent.com/lyzidiamond/learn-geojson/master/geojson/cupcakes.geojson' 54 | 55 | url_json = readLines(con, warn = FALSE) 56 | 57 | url_file_string = paste(url_json, collapse = "\n") 58 | 59 | close(con); gc() 60 | } 61 | 62 | res = export_From_geojson(url_file_string, Flatten_Coords, Average_Coordinates, To_List) 63 | 64 | return(res) 65 | } 66 | 67 | 68 | 69 | #' reads GeoJson data using a one-word-schema 70 | #' 71 | #' @param url_file_string a string specifying the input path to a file OR a geojson object (in form of a character string) OR a valid url (beginning with 'http..') pointing to a geojson object 72 | #' @param geometry_name a string specifying the geometry name in the geojson string/file. The \emph{geometry_name} functions as a one-word schema and can significantly speed up the parsing of the data. 73 | #' @param Average_Coordinates either TRUE or FALSE. If TRUE then additionally a geojson-dump and the average latitude and longitude of the geometry object will be returned. 74 | #' @param To_List either TRUE or FALSE. If TRUE then the \emph{coordinates} of the geometry object will be returned in form of a list, otherwise in form of a numeric matrix. 75 | #' @return a (nested) list 76 | #' @details 77 | #' This function is appropriate when the property-names do not match exactly the 'RFC 7946' specification ( for instance if the \emph{geometry} object-name appears as \emph{location} as is the case sometimes in mongodb queries ). 78 | #' The user can then specify the \emph{geometry_name} as it exactly appears in the .geojson string/file (consult the example for more details). If no \emph{geometry_name} is given then recursion will be used, which increases the processing time. 79 | #' In case that the input .geojson object is of \emph{type} : \emph{Point}, \emph{LineString}, \emph{MultiPoint}, \emph{Polygon}, \emph{GeometryCollection}, \emph{MultiLineString}, \emph{MultiPolygon}, 80 | #' \emph{Feature} or \emph{FeatureCollection} with a second attribute name : \emph{coordinates}, then the \emph{geometry_name} parameter is not necessary. 81 | #' @export 82 | #' @examples 83 | #' 84 | #' library(geojsonR) 85 | #' 86 | #' 87 | #' # INPUT IS A GEOJSON (character string) 88 | #' 89 | #' tmp_str = '{ 90 | #' "name" : "example_name", 91 | #' "location" : { 92 | #' "type" : "Point", 93 | #' "coordinates" : [ -120.24, 39.21 ] 94 | #' } 95 | #' }' 96 | #' 97 | #' res = FROM_GeoJson_Schema(url_file_string = tmp_str, geometry_name = "location") 98 | #' 99 | 100 | FROM_GeoJson_Schema = function(url_file_string, geometry_name = "", Average_Coordinates = FALSE, To_List = FALSE) { 101 | 102 | if (!inherits(url_file_string, 'character') && length(url_file_string) != 1) { stop("the 'url_file_string' parameter should be of type character string", call. = F) } 103 | if (!inherits(geometry_name, "character")) { stop("the 'geometry_name' parameter should be of type character", call. = F) } 104 | if (!inherits(Average_Coordinates, "logical")) { stop("the 'Average_Coordinates' parameter should be of type boolean", call. = F) } 105 | if (!inherits(To_List, "logical")) { stop("the 'To_List' parameter should be of type boolean", call. = F) } 106 | 107 | if (substring(url_file_string, 1, 4) == "http") { # only url-addresses which start with 'http' will be considered as valid 108 | 109 | con = url(url_file_string, method = "libcurl") # test url-output with : 'https://raw.githubusercontent.com/lyzidiamond/learn-geojson/master/geojson/cupcakes.geojson' 110 | 111 | url_json = readLines(con, warn = FALSE) 112 | 113 | url_file_string = paste(url_json, collapse = "\n") 114 | 115 | close(con); gc() 116 | } 117 | 118 | res = export_From_geojson_schema(url_file_string, geometry_name, Average_Coordinates, To_List) 119 | 120 | return(res) 121 | } 122 | 123 | 124 | 125 | #' returns a json-dump from a geojson file 126 | #' 127 | #' @param url_file either a string specifying the input path to a file OR a valid url (beginning with 'http..') pointing to a geojson object 128 | #' @return a character string (json dump) 129 | #' @export 130 | #' @examples 131 | #' 132 | #' \dontrun{ 133 | #' 134 | #' library(geojsonR) 135 | #' 136 | #' res = Dump_From_GeoJson("/myfolder/point.geojson") 137 | #' } 138 | #' 139 | 140 | Dump_From_GeoJson = function(url_file) { 141 | 142 | if (!inherits(url_file, 'character') && length(url_file) != 1) { 143 | 144 | stop("the 'url_file' parameter should be of type character string", call. = F) 145 | } 146 | 147 | if (substring(url_file, 1, 4) == "http") { # only url-addresses which start with 'http' will be considered as valid 148 | 149 | con = url(url_file, method = "libcurl") # test url-output with : 'https://raw.githubusercontent.com/lyzidiamond/learn-geojson/master/geojson/hackspots.geojson' 150 | 151 | url_json = readLines(con, warn = FALSE) 152 | 153 | res = paste(url_json, collapse = "\n") 154 | 155 | close(con); gc()} 156 | 157 | else if (file.exists(url_file)) { 158 | 159 | res = dump_geojson(url_file)} 160 | 161 | else { 162 | 163 | stop("the input shoud be either a valid url (beginning with 'http..') OR a valid path to a geojson file", call. = F) 164 | } 165 | 166 | return(res) 167 | } 168 | 169 | 170 | 171 | #' converts data to a GeoJson object 172 | #' 173 | #' @param data a list specifying the geojson geometry object 174 | #' @param stringify either TRUE or FALSE, specifying if the output should also include a geojson-dump (as a character string) 175 | #' @return a List 176 | #' @export 177 | #' @docType class 178 | #' @importFrom R6 R6Class 179 | #' @section Methods: 180 | #' 181 | #' \describe{ 182 | #' \item{\code{TO_GeoJson$new()}}{} 183 | #' 184 | #' \item{\code{--------------}}{} 185 | #' 186 | #' \item{\code{Point(data, stringify = FALSE)}}{} 187 | #' 188 | #' \item{\code{--------------}}{} 189 | #' 190 | #' \item{\code{MultiPoint(data, stringify = FALSE)}}{} 191 | #' 192 | #' \item{\code{--------------}}{} 193 | #' 194 | #' \item{\code{LineString(data, stringify = FALSE)}}{} 195 | #' 196 | #' \item{\code{--------------}}{} 197 | #' 198 | #' \item{\code{MultiLineString(data, stringify = FALSE)}}{} 199 | #' 200 | #' \item{\code{--------------}}{} 201 | #' 202 | #' \item{\code{Polygon(data, stringify = FALSE)}}{} 203 | #' 204 | #' \item{\code{--------------}}{} 205 | #' 206 | #' \item{\code{MultiPolygon(data, stringify = FALSE)}}{} 207 | #' 208 | #' \item{\code{--------------}}{} 209 | #' 210 | #' \item{\code{GeometryCollection(data, stringify = FALSE)}}{} 211 | #' 212 | #' \item{\code{--------------}}{} 213 | #' 214 | #' \item{\code{Feature(data, stringify = FALSE)}}{} 215 | #' 216 | #' \item{\code{--------------}}{} 217 | #' 218 | #' \item{\code{FeatureCollection(data, stringify = FALSE)}}{} 219 | #' 220 | #' \item{\code{--------------}}{} 221 | #' } 222 | #' 223 | #' @usage # utl <- TO_GeoJson$new() 224 | #' @examples 225 | #' 226 | #' library(geojsonR) 227 | #' 228 | #' 229 | #'# initialize class 230 | #' 231 | #' init = TO_GeoJson$new() 232 | #' 233 | #' 234 | #' # Examples covering all geometry-objects 235 | #' 236 | #' 237 | #' # Point 238 | #' 239 | #' point_dat = c(100, 1.01) 240 | #' 241 | #' point = init$Point(point_dat, stringify = TRUE) 242 | #' point 243 | #' 244 | #' 245 | #' # MultiPoint 246 | #' 247 | #' multi_point_dat = list(c(100, 1.01), c(200, 2.01)) 248 | #' 249 | #' multi_point = init$MultiPoint(multi_point_dat, stringify = TRUE) 250 | #' multi_point 251 | #' 252 | #' 253 | #' # LineString 254 | #' 255 | #' linestring_dat = list(c(100, 1.01), c(200, 2.01)) 256 | #' 257 | #' line_string = init$LineString(linestring_dat, stringify = TRUE) 258 | #' line_string 259 | #' 260 | #' 261 | #' # MultiLineString 262 | #' 263 | #' multilinestring_dat = list(list(c(100, 0.0), c(101, 1.0)), list(c(102, 2.0), c(103, 3.0))) 264 | #' 265 | #' multiline_string = init$MultiLineString(multilinestring_dat, stringify = TRUE) 266 | #' multiline_string 267 | #' 268 | #' 269 | #' # Polygon (WITHOUT interior rings) 270 | #' 271 | #' polygon_WITHOUT_dat = list(list(c(100, 1.01), c(200, 2.01), c(100, 1.0), c(100, 1.01))) 272 | #' 273 | #' polygon_without = init$Polygon(polygon_WITHOUT_dat, stringify = TRUE) 274 | #' polygon_without 275 | #' 276 | #' 277 | #' # Polygon (WITH interior rings) 278 | #' 279 | #' polygon_WITH_dat = list(list(c(100, 1.01), c(200, 2.01), c(100, 1.0), c(100, 1.01)), 280 | #' 281 | #' list(c(50, 0.5), c(50, 0.8), c(50, 0.9), c(50, 0.5))) 282 | #' 283 | #' polygon_with = init$Polygon(polygon_WITH_dat, stringify = TRUE) 284 | #' polygon_with 285 | #' 286 | #' 287 | #' # MultiPolygon 288 | #' 289 | #' # the first polygon is without interior rings and the second one is with interior rings 290 | #' 291 | #' multi_polygon_dat = list(list(list(c(102, 2.0), c(103, 2.0), c(103, 3.0), c(102, 2.0))), 292 | #' 293 | #' list(list(c(100, 0.0), c(101, 1.0), c(101, 1.0), c(100, 0.0)), 294 | #' 295 | #' list(c(100.2, 0.2), c(100.2, 0.8), c(100.8, 0.8), c(100.2, 0.2)))) 296 | #' 297 | #' multi_polygon = init$MultiPolygon(multi_polygon_dat, stringify = TRUE) 298 | #' multi_polygon 299 | #' 300 | #' 301 | #' 302 | #' # GeometryCollection (named list) 303 | #' 304 | #' 305 | #' Point = c(100, 1.01) 306 | #' 307 | #' MultiPoint = list(c(100, 1.01), c(200, 2.01)) 308 | #' 309 | #' MultiLineString = list(list(c(100, 0.0), c(101, 1.0)), 310 | #' 311 | #' list(c(102, 2.0), c(103, 3.0))) 312 | #' 313 | #' LineString = list(c(100, 1.01), c(200, 2.01)) 314 | #' 315 | #' MultiLineString = list(list(c(100, 0.0), c(101, 1.0)), 316 | #' 317 | #' list(c(102, 2.0), c(103, 3.0))) 318 | #' 319 | #' Polygon = list(list(c(100, 1.01), c(200, 2.01), c(100, 1.0), c(100, 1.01))) 320 | #' 321 | #' Polygon = list(list(c(100, 1.01), c(200, 2.01), c(100, 1.0), c(100, 1.01)), 322 | #' 323 | #' list(c(50, 0.5), c(50, 0.8), c(50, 0.9), c(50, 0.5))) 324 | #' 325 | #' MultiPolygon = list(list(list(c(102, 2.0), c(103, 2.0), c(103, 3.0), c(102, 2.0))), 326 | #' 327 | #' list(list(c(100, 0.0), c(101, 1.0), c(101, 1.0), c(100, 0.0)), 328 | #' 329 | #' list(c(100.2, 0.2), c(100.2, 0.8), c(100.8, 0.8), c(100.2, 0.2)))) 330 | #' 331 | #' 332 | #' geometry_collection_dat = list(Point = Point, MultiPoint = MultiPoint, 333 | #' 334 | #' MultiLineString = MultiLineString, LineString = LineString, 335 | #' 336 | #' MultiLineString = MultiLineString, Polygon = Polygon, 337 | #' 338 | #' Polygon = Polygon, MultiPolygon = MultiPolygon) 339 | #' 340 | #' 341 | #' geometry_col = init$GeometryCollection(geometry_collection_dat, stringify = TRUE) 342 | #' geometry_col 343 | #' 344 | #' 345 | #' # Feature (named list) 346 | #' 347 | #' 348 | #' # Empty 'properties' list 349 | #' 350 | #' feature_dat1 = list(id = 1, bbox = c(1,2,3,4), geometry = list(Point = c(100, 1.01)), 351 | #' 352 | #' properties = list()) 353 | #' 354 | #' 355 | #' # Nested 'properties' list 356 | #' 357 | #' feature_dat2 = list(id = "1", bbox = c(1,2,3,4), geometry = list(Point = c(100, 1.01)), 358 | #' 359 | #' properties = list(prop0 = 'value0', 360 | #' 361 | #' prop1 = 0.0, vec = c(1,2,3), lst = list(a = 1, d = 2))) 362 | #' 363 | #' 364 | #' feature_obj = init$Feature(feature_dat2, stringify = TRUE) 365 | #' feature_obj 366 | #' cat(feature_obj$json_dump) 367 | #' 368 | #' 369 | #' 370 | #' # FeatureCollection (named list) 371 | #' 372 | #' 373 | #' # takes as input the previously created 'feature_dat1', 'feature_dat2' 374 | #' 375 | #' feature_col_dat = list(bbox = c(-10.01, -10.01, 10.01, 10.01), 376 | #' 377 | #' features = list(Feature = feature_dat1, Feature = feature_dat2)) 378 | #' feature_col_dat 379 | #' 380 | #' 381 | #' feature_collection_obj = init$FeatureCollection(feature_col_dat, stringify = TRUE) 382 | #' feature_collection_obj 383 | #' cat(feature_collection_obj$json_dump) 384 | #' 385 | 386 | TO_GeoJson <- R6::R6Class("TO_GeoJson", 387 | 388 | public = list( 389 | 390 | initialize = function() { 391 | 392 | private$empty_vec = numeric(0) 393 | 394 | }, 395 | 396 | Point = function(data, stringify = FALSE) { 397 | 398 | if (!inherits(data, c('numeric', 'vector'))) { stop("the 'data' parameter should be a numeric vector", call. = F) } 399 | 400 | if (!inherits(stringify, 'logical')) { stop("the 'stringify' parameter should be of type boolean", call. = F) } 401 | 402 | res = export_To_GeoJson("Point", data, private$empty_vec, private$empty_vec, private$empty_vec, stringify) 403 | 404 | return(res) 405 | }, 406 | 407 | MultiPoint = function(data, stringify = FALSE) { 408 | 409 | if (!inherits(data, c('numeric', 'list'))) { stop("the 'data' parameter should be a numeric list", call. = F) } 410 | 411 | if (!inherits(stringify, 'logical')) { stop("the 'stringify' parameter should be of type boolean", call. = F) } 412 | 413 | res = export_To_GeoJson("MultiPoint", private$empty_vec, data, private$empty_vec, private$empty_vec, stringify) 414 | 415 | return(res) 416 | }, 417 | 418 | LineString = function(data, stringify = FALSE) { 419 | 420 | if (!inherits(data, c('numeric', 'list'))) { stop("the 'data' parameter should be a numeric list", call. = F) } 421 | 422 | if (!inherits(stringify, 'logical')) { stop("the 'stringify' parameter should be of type boolean", call. = F) } 423 | 424 | res = export_To_GeoJson("LineString", private$empty_vec, data, private$empty_vec, private$empty_vec, stringify) 425 | 426 | return(res) 427 | }, 428 | 429 | MultiLineString = function(data, stringify = FALSE) { 430 | 431 | if (!inherits(data, c('numeric', 'list'))) { stop("the 'data' parameter should be a numeric list", call. = F) } 432 | 433 | if (!inherits(stringify, 'logical')) { stop("the 'stringify' parameter should be of type boolean", call. = F) } 434 | 435 | res = export_To_GeoJson("MultiLineString", private$empty_vec, private$empty_vec, data, private$empty_vec, stringify) 436 | 437 | return(res) 438 | }, 439 | 440 | Polygon = function(data, stringify = FALSE) { 441 | 442 | if (!inherits(data, c('numeric', 'list'))) { stop("the 'data' parameter should be a numeric list", call. = F) } 443 | 444 | if (!inherits(stringify, 'logical')) { stop("the 'stringify' parameter should be of type boolean", call. = F) } 445 | 446 | res = export_To_GeoJson("Polygon", private$empty_vec, private$empty_vec, data, private$empty_vec, stringify) 447 | 448 | return(res) 449 | }, 450 | 451 | MultiPolygon = function(data, stringify = FALSE) { 452 | 453 | if (!inherits(data, c('numeric', 'list'))) { stop("the 'data' parameter should be a numeric list", call. = F) } 454 | 455 | if (!inherits(stringify, 'logical')) { stop("the 'stringify' parameter should be of type boolean", call. = F) } 456 | 457 | res = export_To_GeoJson("MultiPolygon", private$empty_vec, private$empty_vec, private$empty_vec, data, stringify) 458 | 459 | return(res) 460 | }, 461 | 462 | GeometryCollection = function(data, stringify = FALSE) { 463 | 464 | if (!inherits(data, c('numeric', 'list'))) { stop("the 'data' parameter should be a numeric list", call. = F) } 465 | 466 | if (!inherits(stringify, 'logical')) { stop("the 'stringify' parameter should be of type boolean", call. = F) } 467 | 468 | geometry_names = names(data) 469 | 470 | res = Geom_Collection(geometry_names, data, stringify) 471 | 472 | return(res) 473 | }, 474 | 475 | Feature = function(data, stringify = FALSE) { 476 | 477 | if (!inherits(data, 'list')) { stop("the 'data' parameter should be of type list", call. = F) } 478 | 479 | if (!inherits(stringify, 'logical')) { stop("the 'stringify' parameter should be of type boolean", call. = F) } 480 | 481 | feature_names = names(data) 482 | 483 | res = Feature_Obj(feature_names, data, stringify) 484 | 485 | return(res) 486 | }, 487 | 488 | FeatureCollection = function(data, stringify = FALSE) { 489 | 490 | if (!inherits(data, 'list')) { stop("the 'data' parameter should be of type list", call. = F) } 491 | 492 | if (!inherits(stringify, 'logical')) { stop("the 'stringify' parameter should be of type boolean", call. = F) } 493 | 494 | feature_col_names = names(data) 495 | 496 | res = Feature_collection_Obj(feature_col_names, data, stringify) 497 | 498 | return(res) 499 | } 500 | ), 501 | 502 | private = list( 503 | 504 | empty_vec = NULL 505 | ) 506 | ) 507 | 508 | 509 | 510 | #' creates a FeatureCollection dump from multiple Feature geojson objects 511 | #' 512 | #' @param Features_files_vec a character vector specifying paths to files (Feature geojson objects) 513 | #' @param bbox_vec either NULL or a numeric vector 514 | #' @param write_path either NULL or a character string specifying a valid path to a file ( preferably with a \emph{.geojson extension} ) where the output data will be saved 515 | #' @param verbose a boolean. If TRUE then information will be printed out in the console 516 | #' @return a FeatureCollection dump 517 | #' @details 518 | #' The \emph{Features_2Collection} function utilizes internally a for-loop. In case of an error set the \emph{verbose} parameter to TRUE to find out which file leads to this error. 519 | #' @export 520 | #' @examples 521 | #' 522 | #' \dontrun{ 523 | #' 524 | #' library(geojsonR) 525 | #' 526 | #' vec_files = c("/myfolder/Feature1.geojson", "/myfolder/Feature2.geojson", 527 | #' "/myfolder/Feature3.geojson", "/myfolder/Feature4.geojson", 528 | #' "/myfolder/Feature5.geojson") 529 | #' 530 | #' res = Features_2Collection(vec_files, bbox_vec = NULL) 531 | #' } 532 | #' 533 | 534 | Features_2Collection = function(Features_files_vec, bbox_vec = NULL, write_path = NULL, verbose = FALSE) { 535 | 536 | if (!inherits(Features_files_vec, c('vector', 'character'))) { 537 | 538 | stop("the 'Features_files_vec' parameter should be a character vector", call. = F) 539 | } 540 | 541 | if (is.null(bbox_vec)) { 542 | 543 | bbox_vec = numeric(0)} 544 | 545 | else { 546 | 547 | if (!inherits(bbox_vec, c('vector', 'numeric'))) { 548 | 549 | stop("the 'bbox' parameter should be a numeric vector", call. = F) 550 | } 551 | } 552 | 553 | tmp_feat = Features_TO_Collection(Features_files_vec, bbox_vec, verbose) 554 | 555 | if (!is.null(write_path)) { 556 | fileConn = file(write_path) 557 | writeLines(tmp_feat, fileConn) 558 | close(fileConn) 559 | } 560 | 561 | return(tmp_feat) 562 | } 563 | 564 | 565 | 566 | 567 | #' creates a FeatureCollection from R list objects ( see the details section about the limitations of this function ) 568 | #' 569 | #' 570 | #' @param input_list a list object that includes 1 or more geojson R list Features 571 | #' @param path_to_file either an empty string ("") or a valid path to a file where the output FeatureCollection will be saved 572 | #' @param verbose a boolean. If TRUE then information will be printed out in the console 573 | #' @return a FeatureCollection in form of a character string 574 | #' @return a FeatureCollection saved in a file 575 | #' @details 576 | #' 577 | #' \itemize{ 578 | #' \item it allows the following attributes: \emph{'type'}, \emph{'id'}, \emph{'properties'} and \emph{'geometry'} 579 | #' \item it allows only coordinates of type \emph{'Polygon'} or \emph{'MultiPolygon'} to be processed. In case of a \emph{'Polygon'} there are 2 cases: (a.) Polygon WITHOUT interior rings (a numeric matrix is expected) and (b.) Polygon WITH interior rings (a list of numeric matrices is expected). See the test-cases if you receive an error for the correct format of the input data. In case of a \emph{'MultiPolygon'} both Polygons with OR without interior rings can be included. Multipolygons are of the form: list of lists where each SUBLIST can be either a numeric matrix (Polygon without interior rings) or a list (Polygon with interior rings) 580 | #' \item the \emph{properties} attribute must be a list that can take only \emph{character strings}, \emph{numeric} and \emph{integer} values of SIZE 1. In case that any of the input properties is of SIZE > 1 then it will throw an error. 581 | #' } 582 | #' 583 | #' The \emph{input_list} parameter can be EITHER created from scratch OR GeoJson Features (in form of a FeatureCollection) can be loaded in R and modified so that this list can be processed by this function 584 | #' 585 | #' @export 586 | #' @examples 587 | #' 588 | #' \dontrun{ 589 | #' 590 | #' library(geojsonR) 591 | #' 592 | #' #------------------------------------------------ 593 | #' # valid example that will save the data to a file 594 | #' #------------------------------------------------ 595 | #' 596 | #' Feature1 = list(type ="Feature", 597 | #' id = 1L, 598 | #' properties = list(prop1 = 'id', prop2 = 1.0234), 599 | #' geometry = list(type = 'Polygon', 600 | #' coordinates = matrix(runif(20), nrow = 10, ncol = 2))) 601 | #' 602 | #' Feature2 = list(type ="Feature", 603 | #' id = 2L, 604 | #' properties = list(prop1 = 'non-id', prop2 = 6.0987), 605 | #' geometry = list(type = 'MultiPolygon', 606 | #' coordinates = list(matrix(runif(20), nrow = 10, ncol = 2), 607 | #' matrix(runif(20), nrow = 10, ncol = 2)))) 608 | #' 609 | #' list_features = list(Feature1, Feature2) 610 | #' 611 | #' path_feat_col = tempfile(fileext = '.geojson') 612 | #' 613 | #' res = save_R_list_Features_2_FeatureCollection(input_list = list_features, 614 | #' path_to_file = path_feat_col, 615 | #' verbose = TRUE) 616 | #' 617 | #' #------------------------------------- 618 | #' # validate that the file can be loaded 619 | #' #------------------------------------- 620 | #' 621 | #' res_load = FROM_GeoJson_Schema(url_file_string = path_feat_col) 622 | #' str(res_load) 623 | #' 624 | #' 625 | #' #---------------------------------------------------- 626 | #' # INVALID data types such as NA's will throw an ERROR 627 | #' #---------------------------------------------------- 628 | #' 629 | #' 630 | #' Feature1 = list(type ="Feature", 631 | #' id = 1L, 632 | #' properties = list(prop1 = NA, prop2 = 1.0234), 633 | #' geometry = list(type = 'Polygon', 634 | #' coordinates = matrix(runif(20), nrow = 10, ncol = 2))) 635 | #' 636 | #' list_features = list(Feature1, Feature2) 637 | #' 638 | #' path_feat_col = tempfile(fileext = '.geojson') 639 | #' 640 | #' res = save_R_list_Features_2_FeatureCollection(input_list = list_features, 641 | #' path_to_file = path_feat_col, 642 | #' verbose = TRUE) 643 | #' } 644 | #' 645 | 646 | save_R_list_Features_2_FeatureCollection = function(input_list, 647 | path_to_file = "", 648 | verbose = FALSE) { 649 | 650 | if (length(input_list) < 1) { 651 | stop("The 'input_list' parameter must be at least of length 1!", call. = F) 652 | } 653 | 654 | res = SAVE_R_list_Features_2_FeatureCollection(x = input_list, 655 | path_to_file = path_to_file, 656 | verbose = verbose) 657 | return(res) 658 | } 659 | 660 | 661 | 662 | 663 | #' secondary function for shiny Applications 664 | #' 665 | #' @param input_file a character string specifying a path to a file 666 | #' @return a (nested) list 667 | #' @details 668 | #' This function is meant for \emph{shiny Applications}. To read a GeoJson file use either the \emph{FROM_GeoJson} or \emph{FROM_GeoJson_Schema} function. 669 | #' @export 670 | 671 | shiny_from_JSON = function(input_file) { # shiny apps should be in a separate package if Rcpp::class(es) are present 672 | 673 | if (!inherits(input_file, 'character') && length(input_file) != 1) { 674 | 675 | stop("the 'input_file' parameter should be a character string", call. = F) 676 | } 677 | 678 | return(export_From_JSON(input_file)) 679 | } 680 | 681 | 682 | 683 | 684 | #' merge json files (or any kind of text files) from a directory 685 | #' 686 | #' @param INPUT_FOLDER a character string specifying a path to the input folder 687 | #' @param OUTPUT_FILE a character string specifying a path to the output file 688 | #' @param CONCAT_DELIMITER a character string specifying the delimiter to use when merging the files 689 | #' @param verbose either TRUE or FALSE. If TRUE then information will be printed in the console. 690 | #' @details 691 | #' This function is meant for json files but it can be applied to any kind of text files. It takes an input folder (\emph{INPUT_FOLDER}) and an output file 692 | #' (\emph{OUTPUT_FILE}) and merges all files from the \emph{INPUT_FOLDER} to a single \emph{OUTPUT_FILE} using the concatenation delimiter (\emph{CONCAT_DELIMITER}). 693 | #' @export 694 | #' @examples 695 | #' 696 | #' \dontrun{ 697 | #' library(geojsonR) 698 | #' 699 | #' merge_files(INPUT_FOLDER = "/my_folder/", OUTPUT_FILE = "output_file.json") 700 | #' } 701 | 702 | merge_files = function(INPUT_FOLDER, OUTPUT_FILE, CONCAT_DELIMITER = "\n", verbose = FALSE) { 703 | 704 | if (!inherits(INPUT_FOLDER, 'character') && length(INPUT_FOLDER) != 1) stop("the 'INPUT_FOLDER' parameter should be a character string", call. = F) 705 | if (!inherits(OUTPUT_FILE, 'character') && length(OUTPUT_FILE) != 1) stop("the 'OUTPUT_FILE' parameter should be a character string", call. = F) 706 | if (!inherits(CONCAT_DELIMITER, 'character') && length(CONCAT_DELIMITER) != 1) stop("the 'CONCAT_DELIMITER' parameter should be a character string", call. = F) 707 | if (!inherits(verbose, 'logical')) stop("the 'verbose' parameter should be of type boolean", call. = F) 708 | 709 | str_SPL = strsplit(INPUT_FOLDER, "")[[1]] 710 | if (!str_SPL[nchar(INPUT_FOLDER)] %in% c("/", "\\")) stop('the "INPUT_FOLDER" parameter should end in slash', call. = F) 711 | 712 | if (file.exists(OUTPUT_FILE)) warning(paste("the '", OUTPUT_FILE, "' file already exists. New data will be added to the end of '", OUTPUT_FILE, "' !", sep = ""), call. = F) 713 | if (!dir.exists(INPUT_FOLDER)) stop("the path to the 'INPUT_FOLDER' parameter does not exist", call. = F) 714 | 715 | merge_json(INPUT_FOLDER, OUTPUT_FILE, CONCAT_DELIMITER, verbose) 716 | 717 | invisible() 718 | } 719 | 720 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![tic](https://github.com/mlampros/geojsonR/workflows/tic/badge.svg?branch=master)](https://github.com/mlampros/geojsonR/actions) 3 | [![codecov.io](https://codecov.io/github/mlampros/geojsonR/coverage.svg?branch=master)](https://codecov.io/github/mlampros/geojsonR?branch=master) 4 | [![CRAN_Status_Badge](http://www.r-pkg.org/badges/version/geojsonR)](http://cran.r-project.org/package=geojsonR) 5 | [![Downloads](http://cranlogs.r-pkg.org/badges/grand-total/geojsonR?color=blue)](http://www.r-pkg.org/pkg/geojsonR) 6 | Buy Me A Coffee 7 | [![Dependencies](https://tinyverse.netlify.com/badge/geojsonR)](https://cran.r-project.org/package=geojsonR) 8 | 9 | 10 | ## geojsonR 11 |
12 | 13 | The **geojsonR** package includes functions for processing [GeoJson objects](https://en.wikipedia.org/wiki/GeoJSON) relying on [RFC 7946](https://datatracker.ietf.org/doc/html/rfc7946). The geojson encoding is based on [json11](https://github.com/dropbox/json11), a tiny JSON library for C++11. Furthermore, the source code is exported in R through the *Rcpp* and *RcppArmadillo* packages. More details on the functionality of geojsonR can be found in the [blog-post](http://mlampros.github.io/2017/03/29/geojsonR_package/) and in the package Vignette. 14 |

15 | 16 | To install the package from CRAN use, 17 | 18 | ```R 19 | 20 | install.packages("geojsonR") 21 | 22 | 23 | ``` 24 |
25 | 26 | and to download the latest version from Github use the *install_github* function of the *remotes* package, 27 |

28 | 29 | ```R 30 | 31 | remotes::install_github('mlampros/geojsonR') 32 | 33 | 34 | ``` 35 |
36 | 37 | Use the following link to report bugs/issues, 38 |

39 | 40 | [https://github.com/mlampros/geojsonR/issues](https://github.com/mlampros/geojsonR/issues) 41 | 42 |
43 | 44 | ### **Citation:** 45 | 46 | If you use the code of this repository in your paper or research please cite both **geojsonR** and the **original articles / software** `https://CRAN.R-project.org/package=geojsonR`: 47 | 48 |
49 | 50 | ```R 51 | @Manual{, 52 | title = {{geojsonR}: A GeoJson Processing Toolkit}, 53 | author = {Lampros Mouselimis}, 54 | year = {2021}, 55 | note = {R package version 1.1.2}, 56 | url = {https://CRAN.R-project.org/package=geojsonR}, 57 | } 58 | ``` 59 | 60 |
61 | -------------------------------------------------------------------------------- /inst/CITATION: -------------------------------------------------------------------------------- 1 | citHeader("Please cite both the package and the original articles / software in your publications:") 2 | 3 | year <- sub("-.*", "", meta$Date) 4 | note <- sprintf("R package version %s", meta$Version) 5 | 6 | bibentry( 7 | bibtype = "Manual", 8 | title = "{geojsonR}: A GeoJson Processing Toolkit", 9 | author = person("Lampros", "Mouselimis"), 10 | year = year, 11 | note = note, 12 | url = "https://CRAN.R-project.org/package=geojsonR" 13 | ) 14 | 15 | bibentry( 16 | bibtype = "Manual", 17 | title = "{json11}: A tiny JSON library for C++11, providing JSON parsing and serialization", 18 | author = person("Dropbox", "Inc"), 19 | year = "2013", 20 | url = "https://github.com/dropbox/json11" 21 | ) 22 | -------------------------------------------------------------------------------- /inst/COPYRIGHTS: -------------------------------------------------------------------------------- 1 | 2 | 3 | ======================================== 4 | 'json11' : A tiny JSON library for C++11 5 | ======================================== 6 | 7 | 8 | Copyright (c) 2013 Dropbox, Inc. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | THE SOFTWARE. 27 | -------------------------------------------------------------------------------- /man/Dump_From_GeoJson.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{Dump_From_GeoJson} 4 | \alias{Dump_From_GeoJson} 5 | \title{returns a json-dump from a geojson file} 6 | \usage{ 7 | Dump_From_GeoJson(url_file) 8 | } 9 | \arguments{ 10 | \item{url_file}{either a string specifying the input path to a file OR a valid url (beginning with 'http..') pointing to a geojson object} 11 | } 12 | \value{ 13 | a character string (json dump) 14 | } 15 | \description{ 16 | returns a json-dump from a geojson file 17 | } 18 | \examples{ 19 | 20 | \dontrun{ 21 | 22 | library(geojsonR) 23 | 24 | res = Dump_From_GeoJson("/myfolder/point.geojson") 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /man/FROM_GeoJson.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{FROM_GeoJson} 4 | \alias{FROM_GeoJson} 5 | \title{reads GeoJson data} 6 | \usage{ 7 | FROM_GeoJson( 8 | url_file_string, 9 | Flatten_Coords = FALSE, 10 | Average_Coordinates = FALSE, 11 | To_List = FALSE 12 | ) 13 | } 14 | \arguments{ 15 | \item{url_file_string}{a string specifying the input path to a file OR a geojson object (in form of a character string) OR a valid url (beginning with 'http..') pointing to a geojson object} 16 | 17 | \item{Flatten_Coords}{either TRUE or FALSE. If TRUE then the properties member of the geojson file will be omitted during parsing.} 18 | 19 | \item{Average_Coordinates}{either TRUE or FALSE. If TRUE then additionally a geojson-dump and the average latitude and longitude of the geometry object will be returned.} 20 | 21 | \item{To_List}{either TRUE or FALSE. If TRUE then the \emph{coordinates} of the geometry object will be returned in form of a list, otherwise in form of a numeric matrix.} 22 | } 23 | \value{ 24 | a (nested) list 25 | } 26 | \description{ 27 | reads GeoJson data 28 | } 29 | \details{ 30 | The \emph{FROM_GeoJson} function is based on the 'RFC 7946' specification. Thus, geojson files/strings which include property-names other than the 'RFC 7946' specifies will return an error. To avoid errors of 31 | that kind a user should take advantage of the \emph{FROM_GeoJson_Schema} function, which is not as strict concerning the property names. 32 | } 33 | \examples{ 34 | 35 | \dontrun{ 36 | 37 | library(geojsonR) 38 | 39 | 40 | # INPUT IS A FILE 41 | 42 | res = FROM_GeoJson(url_file_string = "/myfolder/feature_collection.geojson") 43 | 44 | 45 | # INPUT IS A GEOJSON (character string) 46 | 47 | tmp_str = '{ "type": "MultiPolygon", "coordinates": [ 48 | [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]], 49 | [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]], 50 | [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]] 51 | ] 52 | }' 53 | 54 | res = FROM_GeoJson(url_file_string = tmp_str) 55 | 56 | 57 | # INPUT IS A URL 58 | 59 | res = FROM_GeoJson(url_file_string = "http://www.EXAMPLE_web_page.geojson") 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /man/FROM_GeoJson_Schema.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{FROM_GeoJson_Schema} 4 | \alias{FROM_GeoJson_Schema} 5 | \title{reads GeoJson data using a one-word-schema} 6 | \usage{ 7 | FROM_GeoJson_Schema( 8 | url_file_string, 9 | geometry_name = "", 10 | Average_Coordinates = FALSE, 11 | To_List = FALSE 12 | ) 13 | } 14 | \arguments{ 15 | \item{url_file_string}{a string specifying the input path to a file OR a geojson object (in form of a character string) OR a valid url (beginning with 'http..') pointing to a geojson object} 16 | 17 | \item{geometry_name}{a string specifying the geometry name in the geojson string/file. The \emph{geometry_name} functions as a one-word schema and can significantly speed up the parsing of the data.} 18 | 19 | \item{Average_Coordinates}{either TRUE or FALSE. If TRUE then additionally a geojson-dump and the average latitude and longitude of the geometry object will be returned.} 20 | 21 | \item{To_List}{either TRUE or FALSE. If TRUE then the \emph{coordinates} of the geometry object will be returned in form of a list, otherwise in form of a numeric matrix.} 22 | } 23 | \value{ 24 | a (nested) list 25 | } 26 | \description{ 27 | reads GeoJson data using a one-word-schema 28 | } 29 | \details{ 30 | This function is appropriate when the property-names do not match exactly the 'RFC 7946' specification ( for instance if the \emph{geometry} object-name appears as \emph{location} as is the case sometimes in mongodb queries ). 31 | The user can then specify the \emph{geometry_name} as it exactly appears in the .geojson string/file (consult the example for more details). If no \emph{geometry_name} is given then recursion will be used, which increases the processing time. 32 | In case that the input .geojson object is of \emph{type} : \emph{Point}, \emph{LineString}, \emph{MultiPoint}, \emph{Polygon}, \emph{GeometryCollection}, \emph{MultiLineString}, \emph{MultiPolygon}, 33 | \emph{Feature} or \emph{FeatureCollection} with a second attribute name : \emph{coordinates}, then the \emph{geometry_name} parameter is not necessary. 34 | } 35 | \examples{ 36 | 37 | library(geojsonR) 38 | 39 | 40 | # INPUT IS A GEOJSON (character string) 41 | 42 | tmp_str = '{ 43 | "name" : "example_name", 44 | "location" : { 45 | "type" : "Point", 46 | "coordinates" : [ -120.24, 39.21 ] 47 | } 48 | }' 49 | 50 | res = FROM_GeoJson_Schema(url_file_string = tmp_str, geometry_name = "location") 51 | 52 | } 53 | -------------------------------------------------------------------------------- /man/Features_2Collection.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{Features_2Collection} 4 | \alias{Features_2Collection} 5 | \title{creates a FeatureCollection dump from multiple Feature geojson objects} 6 | \usage{ 7 | Features_2Collection( 8 | Features_files_vec, 9 | bbox_vec = NULL, 10 | write_path = NULL, 11 | verbose = FALSE 12 | ) 13 | } 14 | \arguments{ 15 | \item{Features_files_vec}{a character vector specifying paths to files (Feature geojson objects)} 16 | 17 | \item{bbox_vec}{either NULL or a numeric vector} 18 | 19 | \item{write_path}{either NULL or a character string specifying a valid path to a file ( preferably with a \emph{.geojson extension} ) where the output data will be saved} 20 | 21 | \item{verbose}{a boolean. If TRUE then information will be printed out in the console} 22 | } 23 | \value{ 24 | a FeatureCollection dump 25 | } 26 | \description{ 27 | creates a FeatureCollection dump from multiple Feature geojson objects 28 | } 29 | \details{ 30 | The \emph{Features_2Collection} function utilizes internally a for-loop. In case of an error set the \emph{verbose} parameter to TRUE to find out which file leads to this error. 31 | } 32 | \examples{ 33 | 34 | \dontrun{ 35 | 36 | library(geojsonR) 37 | 38 | vec_files = c("/myfolder/Feature1.geojson", "/myfolder/Feature2.geojson", 39 | "/myfolder/Feature3.geojson", "/myfolder/Feature4.geojson", 40 | "/myfolder/Feature5.geojson") 41 | 42 | res = Features_2Collection(vec_files, bbox_vec = NULL) 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /man/TO_GeoJson.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \docType{class} 4 | \name{TO_GeoJson} 5 | \alias{TO_GeoJson} 6 | \title{converts data to a GeoJson object} 7 | \usage{ 8 | # utl <- TO_GeoJson$new() 9 | } 10 | \value{ 11 | a List 12 | } 13 | \description{ 14 | converts data to a GeoJson object 15 | 16 | converts data to a GeoJson object 17 | } 18 | \section{Methods}{ 19 | 20 | 21 | \describe{ 22 | \item{\code{TO_GeoJson$new()}}{} 23 | 24 | \item{\code{--------------}}{} 25 | 26 | \item{\code{Point(data, stringify = FALSE)}}{} 27 | 28 | \item{\code{--------------}}{} 29 | 30 | \item{\code{MultiPoint(data, stringify = FALSE)}}{} 31 | 32 | \item{\code{--------------}}{} 33 | 34 | \item{\code{LineString(data, stringify = FALSE)}}{} 35 | 36 | \item{\code{--------------}}{} 37 | 38 | \item{\code{MultiLineString(data, stringify = FALSE)}}{} 39 | 40 | \item{\code{--------------}}{} 41 | 42 | \item{\code{Polygon(data, stringify = FALSE)}}{} 43 | 44 | \item{\code{--------------}}{} 45 | 46 | \item{\code{MultiPolygon(data, stringify = FALSE)}}{} 47 | 48 | \item{\code{--------------}}{} 49 | 50 | \item{\code{GeometryCollection(data, stringify = FALSE)}}{} 51 | 52 | \item{\code{--------------}}{} 53 | 54 | \item{\code{Feature(data, stringify = FALSE)}}{} 55 | 56 | \item{\code{--------------}}{} 57 | 58 | \item{\code{FeatureCollection(data, stringify = FALSE)}}{} 59 | 60 | \item{\code{--------------}}{} 61 | } 62 | } 63 | 64 | \examples{ 65 | 66 | library(geojsonR) 67 | 68 | 69 | # initialize class 70 | 71 | init = TO_GeoJson$new() 72 | 73 | 74 | # Examples covering all geometry-objects 75 | 76 | 77 | # Point 78 | 79 | point_dat = c(100, 1.01) 80 | 81 | point = init$Point(point_dat, stringify = TRUE) 82 | point 83 | 84 | 85 | # MultiPoint 86 | 87 | multi_point_dat = list(c(100, 1.01), c(200, 2.01)) 88 | 89 | multi_point = init$MultiPoint(multi_point_dat, stringify = TRUE) 90 | multi_point 91 | 92 | 93 | # LineString 94 | 95 | linestring_dat = list(c(100, 1.01), c(200, 2.01)) 96 | 97 | line_string = init$LineString(linestring_dat, stringify = TRUE) 98 | line_string 99 | 100 | 101 | # MultiLineString 102 | 103 | multilinestring_dat = list(list(c(100, 0.0), c(101, 1.0)), list(c(102, 2.0), c(103, 3.0))) 104 | 105 | multiline_string = init$MultiLineString(multilinestring_dat, stringify = TRUE) 106 | multiline_string 107 | 108 | 109 | # Polygon (WITHOUT interior rings) 110 | 111 | polygon_WITHOUT_dat = list(list(c(100, 1.01), c(200, 2.01), c(100, 1.0), c(100, 1.01))) 112 | 113 | polygon_without = init$Polygon(polygon_WITHOUT_dat, stringify = TRUE) 114 | polygon_without 115 | 116 | 117 | # Polygon (WITH interior rings) 118 | 119 | polygon_WITH_dat = list(list(c(100, 1.01), c(200, 2.01), c(100, 1.0), c(100, 1.01)), 120 | 121 | list(c(50, 0.5), c(50, 0.8), c(50, 0.9), c(50, 0.5))) 122 | 123 | polygon_with = init$Polygon(polygon_WITH_dat, stringify = TRUE) 124 | polygon_with 125 | 126 | 127 | # MultiPolygon 128 | 129 | # the first polygon is without interior rings and the second one is with interior rings 130 | 131 | multi_polygon_dat = list(list(list(c(102, 2.0), c(103, 2.0), c(103, 3.0), c(102, 2.0))), 132 | 133 | list(list(c(100, 0.0), c(101, 1.0), c(101, 1.0), c(100, 0.0)), 134 | 135 | list(c(100.2, 0.2), c(100.2, 0.8), c(100.8, 0.8), c(100.2, 0.2)))) 136 | 137 | multi_polygon = init$MultiPolygon(multi_polygon_dat, stringify = TRUE) 138 | multi_polygon 139 | 140 | 141 | 142 | # GeometryCollection (named list) 143 | 144 | 145 | Point = c(100, 1.01) 146 | 147 | MultiPoint = list(c(100, 1.01), c(200, 2.01)) 148 | 149 | MultiLineString = list(list(c(100, 0.0), c(101, 1.0)), 150 | 151 | list(c(102, 2.0), c(103, 3.0))) 152 | 153 | LineString = list(c(100, 1.01), c(200, 2.01)) 154 | 155 | MultiLineString = list(list(c(100, 0.0), c(101, 1.0)), 156 | 157 | list(c(102, 2.0), c(103, 3.0))) 158 | 159 | Polygon = list(list(c(100, 1.01), c(200, 2.01), c(100, 1.0), c(100, 1.01))) 160 | 161 | Polygon = list(list(c(100, 1.01), c(200, 2.01), c(100, 1.0), c(100, 1.01)), 162 | 163 | list(c(50, 0.5), c(50, 0.8), c(50, 0.9), c(50, 0.5))) 164 | 165 | MultiPolygon = list(list(list(c(102, 2.0), c(103, 2.0), c(103, 3.0), c(102, 2.0))), 166 | 167 | list(list(c(100, 0.0), c(101, 1.0), c(101, 1.0), c(100, 0.0)), 168 | 169 | list(c(100.2, 0.2), c(100.2, 0.8), c(100.8, 0.8), c(100.2, 0.2)))) 170 | 171 | 172 | geometry_collection_dat = list(Point = Point, MultiPoint = MultiPoint, 173 | 174 | MultiLineString = MultiLineString, LineString = LineString, 175 | 176 | MultiLineString = MultiLineString, Polygon = Polygon, 177 | 178 | Polygon = Polygon, MultiPolygon = MultiPolygon) 179 | 180 | 181 | geometry_col = init$GeometryCollection(geometry_collection_dat, stringify = TRUE) 182 | geometry_col 183 | 184 | 185 | # Feature (named list) 186 | 187 | 188 | # Empty 'properties' list 189 | 190 | feature_dat1 = list(id = 1, bbox = c(1,2,3,4), geometry = list(Point = c(100, 1.01)), 191 | 192 | properties = list()) 193 | 194 | 195 | # Nested 'properties' list 196 | 197 | feature_dat2 = list(id = "1", bbox = c(1,2,3,4), geometry = list(Point = c(100, 1.01)), 198 | 199 | properties = list(prop0 = 'value0', 200 | 201 | prop1 = 0.0, vec = c(1,2,3), lst = list(a = 1, d = 2))) 202 | 203 | 204 | feature_obj = init$Feature(feature_dat2, stringify = TRUE) 205 | feature_obj 206 | cat(feature_obj$json_dump) 207 | 208 | 209 | 210 | # FeatureCollection (named list) 211 | 212 | 213 | # takes as input the previously created 'feature_dat1', 'feature_dat2' 214 | 215 | feature_col_dat = list(bbox = c(-10.01, -10.01, 10.01, 10.01), 216 | 217 | features = list(Feature = feature_dat1, Feature = feature_dat2)) 218 | feature_col_dat 219 | 220 | 221 | feature_collection_obj = init$FeatureCollection(feature_col_dat, stringify = TRUE) 222 | feature_collection_obj 223 | cat(feature_collection_obj$json_dump) 224 | 225 | } 226 | \section{Methods}{ 227 | \subsection{Public methods}{ 228 | \itemize{ 229 | \item \href{#method-TO_GeoJson-new}{\code{TO_GeoJson$new()}} 230 | \item \href{#method-TO_GeoJson-Point}{\code{TO_GeoJson$Point()}} 231 | \item \href{#method-TO_GeoJson-MultiPoint}{\code{TO_GeoJson$MultiPoint()}} 232 | \item \href{#method-TO_GeoJson-LineString}{\code{TO_GeoJson$LineString()}} 233 | \item \href{#method-TO_GeoJson-MultiLineString}{\code{TO_GeoJson$MultiLineString()}} 234 | \item \href{#method-TO_GeoJson-Polygon}{\code{TO_GeoJson$Polygon()}} 235 | \item \href{#method-TO_GeoJson-MultiPolygon}{\code{TO_GeoJson$MultiPolygon()}} 236 | \item \href{#method-TO_GeoJson-GeometryCollection}{\code{TO_GeoJson$GeometryCollection()}} 237 | \item \href{#method-TO_GeoJson-Feature}{\code{TO_GeoJson$Feature()}} 238 | \item \href{#method-TO_GeoJson-FeatureCollection}{\code{TO_GeoJson$FeatureCollection()}} 239 | \item \href{#method-TO_GeoJson-clone}{\code{TO_GeoJson$clone()}} 240 | } 241 | } 242 | \if{html}{\out{
}} 243 | \if{html}{\out{}} 244 | \if{latex}{\out{\hypertarget{method-TO_GeoJson-new}{}}} 245 | \subsection{Method \code{new()}}{ 246 | \subsection{Usage}{ 247 | \if{html}{\out{
}}\preformatted{TO_GeoJson$new()}\if{html}{\out{
}} 248 | } 249 | 250 | } 251 | \if{html}{\out{
}} 252 | \if{html}{\out{}} 253 | \if{latex}{\out{\hypertarget{method-TO_GeoJson-Point}{}}} 254 | \subsection{Method \code{Point()}}{ 255 | \subsection{Usage}{ 256 | \if{html}{\out{
}}\preformatted{TO_GeoJson$Point(data, stringify = FALSE)}\if{html}{\out{
}} 257 | } 258 | 259 | \subsection{Arguments}{ 260 | \if{html}{\out{
}} 261 | \describe{ 262 | \item{\code{data}}{a list specifying the geojson geometry object} 263 | 264 | \item{\code{stringify}}{either TRUE or FALSE, specifying if the output should also include a geojson-dump (as a character string)} 265 | } 266 | \if{html}{\out{
}} 267 | } 268 | } 269 | \if{html}{\out{
}} 270 | \if{html}{\out{}} 271 | \if{latex}{\out{\hypertarget{method-TO_GeoJson-MultiPoint}{}}} 272 | \subsection{Method \code{MultiPoint()}}{ 273 | \subsection{Usage}{ 274 | \if{html}{\out{
}}\preformatted{TO_GeoJson$MultiPoint(data, stringify = FALSE)}\if{html}{\out{
}} 275 | } 276 | 277 | \subsection{Arguments}{ 278 | \if{html}{\out{
}} 279 | \describe{ 280 | \item{\code{data}}{a list specifying the geojson geometry object} 281 | 282 | \item{\code{stringify}}{either TRUE or FALSE, specifying if the output should also include a geojson-dump (as a character string)} 283 | } 284 | \if{html}{\out{
}} 285 | } 286 | } 287 | \if{html}{\out{
}} 288 | \if{html}{\out{}} 289 | \if{latex}{\out{\hypertarget{method-TO_GeoJson-LineString}{}}} 290 | \subsection{Method \code{LineString()}}{ 291 | \subsection{Usage}{ 292 | \if{html}{\out{
}}\preformatted{TO_GeoJson$LineString(data, stringify = FALSE)}\if{html}{\out{
}} 293 | } 294 | 295 | \subsection{Arguments}{ 296 | \if{html}{\out{
}} 297 | \describe{ 298 | \item{\code{data}}{a list specifying the geojson geometry object} 299 | 300 | \item{\code{stringify}}{either TRUE or FALSE, specifying if the output should also include a geojson-dump (as a character string)} 301 | } 302 | \if{html}{\out{
}} 303 | } 304 | } 305 | \if{html}{\out{
}} 306 | \if{html}{\out{}} 307 | \if{latex}{\out{\hypertarget{method-TO_GeoJson-MultiLineString}{}}} 308 | \subsection{Method \code{MultiLineString()}}{ 309 | \subsection{Usage}{ 310 | \if{html}{\out{
}}\preformatted{TO_GeoJson$MultiLineString(data, stringify = FALSE)}\if{html}{\out{
}} 311 | } 312 | 313 | \subsection{Arguments}{ 314 | \if{html}{\out{
}} 315 | \describe{ 316 | \item{\code{data}}{a list specifying the geojson geometry object} 317 | 318 | \item{\code{stringify}}{either TRUE or FALSE, specifying if the output should also include a geojson-dump (as a character string)} 319 | } 320 | \if{html}{\out{
}} 321 | } 322 | } 323 | \if{html}{\out{
}} 324 | \if{html}{\out{}} 325 | \if{latex}{\out{\hypertarget{method-TO_GeoJson-Polygon}{}}} 326 | \subsection{Method \code{Polygon()}}{ 327 | \subsection{Usage}{ 328 | \if{html}{\out{
}}\preformatted{TO_GeoJson$Polygon(data, stringify = FALSE)}\if{html}{\out{
}} 329 | } 330 | 331 | \subsection{Arguments}{ 332 | \if{html}{\out{
}} 333 | \describe{ 334 | \item{\code{data}}{a list specifying the geojson geometry object} 335 | 336 | \item{\code{stringify}}{either TRUE or FALSE, specifying if the output should also include a geojson-dump (as a character string)} 337 | } 338 | \if{html}{\out{
}} 339 | } 340 | } 341 | \if{html}{\out{
}} 342 | \if{html}{\out{}} 343 | \if{latex}{\out{\hypertarget{method-TO_GeoJson-MultiPolygon}{}}} 344 | \subsection{Method \code{MultiPolygon()}}{ 345 | \subsection{Usage}{ 346 | \if{html}{\out{
}}\preformatted{TO_GeoJson$MultiPolygon(data, stringify = FALSE)}\if{html}{\out{
}} 347 | } 348 | 349 | \subsection{Arguments}{ 350 | \if{html}{\out{
}} 351 | \describe{ 352 | \item{\code{data}}{a list specifying the geojson geometry object} 353 | 354 | \item{\code{stringify}}{either TRUE or FALSE, specifying if the output should also include a geojson-dump (as a character string)} 355 | } 356 | \if{html}{\out{
}} 357 | } 358 | } 359 | \if{html}{\out{
}} 360 | \if{html}{\out{}} 361 | \if{latex}{\out{\hypertarget{method-TO_GeoJson-GeometryCollection}{}}} 362 | \subsection{Method \code{GeometryCollection()}}{ 363 | \subsection{Usage}{ 364 | \if{html}{\out{
}}\preformatted{TO_GeoJson$GeometryCollection(data, stringify = FALSE)}\if{html}{\out{
}} 365 | } 366 | 367 | \subsection{Arguments}{ 368 | \if{html}{\out{
}} 369 | \describe{ 370 | \item{\code{data}}{a list specifying the geojson geometry object} 371 | 372 | \item{\code{stringify}}{either TRUE or FALSE, specifying if the output should also include a geojson-dump (as a character string)} 373 | } 374 | \if{html}{\out{
}} 375 | } 376 | } 377 | \if{html}{\out{
}} 378 | \if{html}{\out{}} 379 | \if{latex}{\out{\hypertarget{method-TO_GeoJson-Feature}{}}} 380 | \subsection{Method \code{Feature()}}{ 381 | \subsection{Usage}{ 382 | \if{html}{\out{
}}\preformatted{TO_GeoJson$Feature(data, stringify = FALSE)}\if{html}{\out{
}} 383 | } 384 | 385 | \subsection{Arguments}{ 386 | \if{html}{\out{
}} 387 | \describe{ 388 | \item{\code{data}}{a list specifying the geojson geometry object} 389 | 390 | \item{\code{stringify}}{either TRUE or FALSE, specifying if the output should also include a geojson-dump (as a character string)} 391 | } 392 | \if{html}{\out{
}} 393 | } 394 | } 395 | \if{html}{\out{
}} 396 | \if{html}{\out{}} 397 | \if{latex}{\out{\hypertarget{method-TO_GeoJson-FeatureCollection}{}}} 398 | \subsection{Method \code{FeatureCollection()}}{ 399 | \subsection{Usage}{ 400 | \if{html}{\out{
}}\preformatted{TO_GeoJson$FeatureCollection(data, stringify = FALSE)}\if{html}{\out{
}} 401 | } 402 | 403 | \subsection{Arguments}{ 404 | \if{html}{\out{
}} 405 | \describe{ 406 | \item{\code{data}}{a list specifying the geojson geometry object} 407 | 408 | \item{\code{stringify}}{either TRUE or FALSE, specifying if the output should also include a geojson-dump (as a character string)} 409 | } 410 | \if{html}{\out{
}} 411 | } 412 | } 413 | \if{html}{\out{
}} 414 | \if{html}{\out{}} 415 | \if{latex}{\out{\hypertarget{method-TO_GeoJson-clone}{}}} 416 | \subsection{Method \code{clone()}}{ 417 | The objects of this class are cloneable with this method. 418 | \subsection{Usage}{ 419 | \if{html}{\out{
}}\preformatted{TO_GeoJson$clone(deep = FALSE)}\if{html}{\out{
}} 420 | } 421 | 422 | \subsection{Arguments}{ 423 | \if{html}{\out{
}} 424 | \describe{ 425 | \item{\code{deep}}{Whether to make a deep clone.} 426 | } 427 | \if{html}{\out{
}} 428 | } 429 | } 430 | } 431 | -------------------------------------------------------------------------------- /man/merge_files.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{merge_files} 4 | \alias{merge_files} 5 | \title{merge json files (or any kind of text files) from a directory} 6 | \usage{ 7 | merge_files( 8 | INPUT_FOLDER, 9 | OUTPUT_FILE, 10 | CONCAT_DELIMITER = "\\n", 11 | verbose = FALSE 12 | ) 13 | } 14 | \arguments{ 15 | \item{INPUT_FOLDER}{a character string specifying a path to the input folder} 16 | 17 | \item{OUTPUT_FILE}{a character string specifying a path to the output file} 18 | 19 | \item{CONCAT_DELIMITER}{a character string specifying the delimiter to use when merging the files} 20 | 21 | \item{verbose}{either TRUE or FALSE. If TRUE then information will be printed in the console.} 22 | } 23 | \description{ 24 | merge json files (or any kind of text files) from a directory 25 | } 26 | \details{ 27 | This function is meant for json files but it can be applied to any kind of text files. It takes an input folder (\emph{INPUT_FOLDER}) and an output file 28 | (\emph{OUTPUT_FILE}) and merges all files from the \emph{INPUT_FOLDER} to a single \emph{OUTPUT_FILE} using the concatenation delimiter (\emph{CONCAT_DELIMITER}). 29 | } 30 | \examples{ 31 | 32 | \dontrun{ 33 | library(geojsonR) 34 | 35 | merge_files(INPUT_FOLDER = "/my_folder/", OUTPUT_FILE = "output_file.json") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /man/save_R_list_Features_2_FeatureCollection.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{save_R_list_Features_2_FeatureCollection} 4 | \alias{save_R_list_Features_2_FeatureCollection} 5 | \title{creates a FeatureCollection from R list objects ( see the details section about the limitations of this function )} 6 | \usage{ 7 | save_R_list_Features_2_FeatureCollection( 8 | input_list, 9 | path_to_file = "", 10 | verbose = FALSE 11 | ) 12 | } 13 | \arguments{ 14 | \item{input_list}{a list object that includes 1 or more geojson R list Features} 15 | 16 | \item{path_to_file}{either an empty string ("") or a valid path to a file where the output FeatureCollection will be saved} 17 | 18 | \item{verbose}{a boolean. If TRUE then information will be printed out in the console} 19 | } 20 | \value{ 21 | a FeatureCollection in form of a character string 22 | 23 | a FeatureCollection saved in a file 24 | } 25 | \description{ 26 | creates a FeatureCollection from R list objects ( see the details section about the limitations of this function ) 27 | } 28 | \details{ 29 | \itemize{ 30 | \item it allows the following attributes: \emph{'type'}, \emph{'id'}, \emph{'properties'} and \emph{'geometry'} 31 | \item it allows only coordinates of type \emph{'Polygon'} or \emph{'MultiPolygon'} to be processed. In case of a \emph{'Polygon'} there are 2 cases: (a.) Polygon WITHOUT interior rings (a numeric matrix is expected) and (b.) Polygon WITH interior rings (a list of numeric matrices is expected). See the test-cases if you receive an error for the correct format of the input data. In case of a \emph{'MultiPolygon'} both Polygons with OR without interior rings can be included. Multipolygons are of the form: list of lists where each SUBLIST can be either a numeric matrix (Polygon without interior rings) or a list (Polygon with interior rings) 32 | \item the \emph{properties} attribute must be a list that can take only \emph{character strings}, \emph{numeric} and \emph{integer} values of SIZE 1. In case that any of the input properties is of SIZE > 1 then it will throw an error. 33 | } 34 | 35 | The \emph{input_list} parameter can be EITHER created from scratch OR GeoJson Features (in form of a FeatureCollection) can be loaded in R and modified so that this list can be processed by this function 36 | } 37 | \examples{ 38 | 39 | \dontrun{ 40 | 41 | library(geojsonR) 42 | 43 | #------------------------------------------------ 44 | # valid example that will save the data to a file 45 | #------------------------------------------------ 46 | 47 | Feature1 = list(type ="Feature", 48 | id = 1L, 49 | properties = list(prop1 = 'id', prop2 = 1.0234), 50 | geometry = list(type = 'Polygon', 51 | coordinates = matrix(runif(20), nrow = 10, ncol = 2))) 52 | 53 | Feature2 = list(type ="Feature", 54 | id = 2L, 55 | properties = list(prop1 = 'non-id', prop2 = 6.0987), 56 | geometry = list(type = 'MultiPolygon', 57 | coordinates = list(matrix(runif(20), nrow = 10, ncol = 2), 58 | matrix(runif(20), nrow = 10, ncol = 2)))) 59 | 60 | list_features = list(Feature1, Feature2) 61 | 62 | path_feat_col = tempfile(fileext = '.geojson') 63 | 64 | res = save_R_list_Features_2_FeatureCollection(input_list = list_features, 65 | path_to_file = path_feat_col, 66 | verbose = TRUE) 67 | 68 | #------------------------------------- 69 | # validate that the file can be loaded 70 | #------------------------------------- 71 | 72 | res_load = FROM_GeoJson_Schema(url_file_string = path_feat_col) 73 | str(res_load) 74 | 75 | 76 | #---------------------------------------------------- 77 | # INVALID data types such as NA's will throw an ERROR 78 | #---------------------------------------------------- 79 | 80 | 81 | Feature1 = list(type ="Feature", 82 | id = 1L, 83 | properties = list(prop1 = NA, prop2 = 1.0234), 84 | geometry = list(type = 'Polygon', 85 | coordinates = matrix(runif(20), nrow = 10, ncol = 2))) 86 | 87 | list_features = list(Feature1, Feature2) 88 | 89 | path_feat_col = tempfile(fileext = '.geojson') 90 | 91 | res = save_R_list_Features_2_FeatureCollection(input_list = list_features, 92 | path_to_file = path_feat_col, 93 | verbose = TRUE) 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /man/shiny_from_JSON.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{shiny_from_JSON} 4 | \alias{shiny_from_JSON} 5 | \title{secondary function for shiny Applications} 6 | \usage{ 7 | shiny_from_JSON(input_file) 8 | } 9 | \arguments{ 10 | \item{input_file}{a character string specifying a path to a file} 11 | } 12 | \value{ 13 | a (nested) list 14 | } 15 | \description{ 16 | secondary function for shiny Applications 17 | } 18 | \details{ 19 | This function is meant for \emph{shiny Applications}. To read a GeoJson file use either the \emph{FROM_GeoJson} or \emph{FROM_GeoJson_Schema} function. 20 | } 21 | -------------------------------------------------------------------------------- /src/Makevars: -------------------------------------------------------------------------------- 1 | PKG_CXXFLAGS = $(SHLIB_OPENMP_CXXFLAGS) -DNDEBUG 2 | PKG_LIBS = $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) $(SHLIB_OPENMP_CXXFLAGS) 3 | PKG_CPPFLAGS = -I../inst/include/ 4 | 5 | -------------------------------------------------------------------------------- /src/Makevars.win: -------------------------------------------------------------------------------- 1 | PKG_CXXFLAGS = $(SHLIB_OPENMP_CXXFLAGS) -DNDEBUG 2 | PKG_LIBS = $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) $(SHLIB_OPENMP_CXXFLAGS) 3 | PKG_CPPFLAGS = -I../inst/include/ 4 | -------------------------------------------------------------------------------- /src/RcppExports.cpp: -------------------------------------------------------------------------------- 1 | // Generated by using Rcpp::compileAttributes() -> do not edit by hand 2 | // Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 3 | 4 | #include 5 | #include 6 | 7 | using namespace Rcpp; 8 | 9 | #ifdef RCPP_USE_GLOBAL_ROSTREAM 10 | Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get(); 11 | Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); 12 | #endif 13 | 14 | // export_From_geojson 15 | Rcpp::List export_From_geojson(std::string input_file, bool flatten_coords, bool average_coordinates, bool to_list); 16 | RcppExport SEXP _geojsonR_export_From_geojson(SEXP input_fileSEXP, SEXP flatten_coordsSEXP, SEXP average_coordinatesSEXP, SEXP to_listSEXP) { 17 | BEGIN_RCPP 18 | Rcpp::RObject rcpp_result_gen; 19 | Rcpp::RNGScope rcpp_rngScope_gen; 20 | Rcpp::traits::input_parameter< std::string >::type input_file(input_fileSEXP); 21 | Rcpp::traits::input_parameter< bool >::type flatten_coords(flatten_coordsSEXP); 22 | Rcpp::traits::input_parameter< bool >::type average_coordinates(average_coordinatesSEXP); 23 | Rcpp::traits::input_parameter< bool >::type to_list(to_listSEXP); 24 | rcpp_result_gen = Rcpp::wrap(export_From_geojson(input_file, flatten_coords, average_coordinates, to_list)); 25 | return rcpp_result_gen; 26 | END_RCPP 27 | } 28 | // export_From_JSON 29 | SEXP export_From_JSON(std::string input_file); 30 | RcppExport SEXP _geojsonR_export_From_JSON(SEXP input_fileSEXP) { 31 | BEGIN_RCPP 32 | Rcpp::RObject rcpp_result_gen; 33 | Rcpp::RNGScope rcpp_rngScope_gen; 34 | Rcpp::traits::input_parameter< std::string >::type input_file(input_fileSEXP); 35 | rcpp_result_gen = Rcpp::wrap(export_From_JSON(input_file)); 36 | return rcpp_result_gen; 37 | END_RCPP 38 | } 39 | // dump_geojson 40 | std::string dump_geojson(std::string input_data); 41 | RcppExport SEXP _geojsonR_dump_geojson(SEXP input_dataSEXP) { 42 | BEGIN_RCPP 43 | Rcpp::RObject rcpp_result_gen; 44 | Rcpp::RNGScope rcpp_rngScope_gen; 45 | Rcpp::traits::input_parameter< std::string >::type input_data(input_dataSEXP); 46 | rcpp_result_gen = Rcpp::wrap(dump_geojson(input_data)); 47 | return rcpp_result_gen; 48 | END_RCPP 49 | } 50 | // Features_TO_Collection 51 | std::string Features_TO_Collection(std::vector feat_files_lst, std::vector bbox_vec, bool verbose); 52 | RcppExport SEXP _geojsonR_Features_TO_Collection(SEXP feat_files_lstSEXP, SEXP bbox_vecSEXP, SEXP verboseSEXP) { 53 | BEGIN_RCPP 54 | Rcpp::RObject rcpp_result_gen; 55 | Rcpp::RNGScope rcpp_rngScope_gen; 56 | Rcpp::traits::input_parameter< std::vector >::type feat_files_lst(feat_files_lstSEXP); 57 | Rcpp::traits::input_parameter< std::vector >::type bbox_vec(bbox_vecSEXP); 58 | Rcpp::traits::input_parameter< bool >::type verbose(verboseSEXP); 59 | rcpp_result_gen = Rcpp::wrap(Features_TO_Collection(feat_files_lst, bbox_vec, verbose)); 60 | return rcpp_result_gen; 61 | END_RCPP 62 | } 63 | // export_From_geojson_schema 64 | Rcpp::List export_From_geojson_schema(std::string input_file, std::string GEOMETRY_OBJECT_NAME, bool average_coordinates, bool to_list); 65 | RcppExport SEXP _geojsonR_export_From_geojson_schema(SEXP input_fileSEXP, SEXP GEOMETRY_OBJECT_NAMESEXP, SEXP average_coordinatesSEXP, SEXP to_listSEXP) { 66 | BEGIN_RCPP 67 | Rcpp::RObject rcpp_result_gen; 68 | Rcpp::RNGScope rcpp_rngScope_gen; 69 | Rcpp::traits::input_parameter< std::string >::type input_file(input_fileSEXP); 70 | Rcpp::traits::input_parameter< std::string >::type GEOMETRY_OBJECT_NAME(GEOMETRY_OBJECT_NAMESEXP); 71 | Rcpp::traits::input_parameter< bool >::type average_coordinates(average_coordinatesSEXP); 72 | Rcpp::traits::input_parameter< bool >::type to_list(to_listSEXP); 73 | rcpp_result_gen = Rcpp::wrap(export_From_geojson_schema(input_file, GEOMETRY_OBJECT_NAME, average_coordinates, to_list)); 74 | return rcpp_result_gen; 75 | END_RCPP 76 | } 77 | // list_files 78 | std::vector list_files(const std::string& path, bool full_path); 79 | RcppExport SEXP _geojsonR_list_files(SEXP pathSEXP, SEXP full_pathSEXP) { 80 | BEGIN_RCPP 81 | Rcpp::RObject rcpp_result_gen; 82 | Rcpp::RNGScope rcpp_rngScope_gen; 83 | Rcpp::traits::input_parameter< const std::string& >::type path(pathSEXP); 84 | Rcpp::traits::input_parameter< bool >::type full_path(full_pathSEXP); 85 | rcpp_result_gen = Rcpp::wrap(list_files(path, full_path)); 86 | return rcpp_result_gen; 87 | END_RCPP 88 | } 89 | // merge_json 90 | void merge_json(const std::string& input_folder, std::string output_file, std::string concat_delimiter, bool verbose); 91 | RcppExport SEXP _geojsonR_merge_json(SEXP input_folderSEXP, SEXP output_fileSEXP, SEXP concat_delimiterSEXP, SEXP verboseSEXP) { 92 | BEGIN_RCPP 93 | Rcpp::RNGScope rcpp_rngScope_gen; 94 | Rcpp::traits::input_parameter< const std::string& >::type input_folder(input_folderSEXP); 95 | Rcpp::traits::input_parameter< std::string >::type output_file(output_fileSEXP); 96 | Rcpp::traits::input_parameter< std::string >::type concat_delimiter(concat_delimiterSEXP); 97 | Rcpp::traits::input_parameter< bool >::type verbose(verboseSEXP); 98 | merge_json(input_folder, output_file, concat_delimiter, verbose); 99 | return R_NilValue; 100 | END_RCPP 101 | } 102 | // DATA_TYPE 103 | std::string DATA_TYPE(SEXP sublist); 104 | RcppExport SEXP _geojsonR_DATA_TYPE(SEXP sublistSEXP) { 105 | BEGIN_RCPP 106 | Rcpp::RObject rcpp_result_gen; 107 | Rcpp::RNGScope rcpp_rngScope_gen; 108 | Rcpp::traits::input_parameter< SEXP >::type sublist(sublistSEXP); 109 | rcpp_result_gen = Rcpp::wrap(DATA_TYPE(sublist)); 110 | return rcpp_result_gen; 111 | END_RCPP 112 | } 113 | // inner_coords 114 | std::string inner_coords(Rcpp::List geom_lst, int z, bool polygon_interior); 115 | RcppExport SEXP _geojsonR_inner_coords(SEXP geom_lstSEXP, SEXP zSEXP, SEXP polygon_interiorSEXP) { 116 | BEGIN_RCPP 117 | Rcpp::RObject rcpp_result_gen; 118 | Rcpp::RNGScope rcpp_rngScope_gen; 119 | Rcpp::traits::input_parameter< Rcpp::List >::type geom_lst(geom_lstSEXP); 120 | Rcpp::traits::input_parameter< int >::type z(zSEXP); 121 | Rcpp::traits::input_parameter< bool >::type polygon_interior(polygon_interiorSEXP); 122 | rcpp_result_gen = Rcpp::wrap(inner_coords(geom_lst, z, polygon_interior)); 123 | return rcpp_result_gen; 124 | END_RCPP 125 | } 126 | // Polygon_with_interior_rings 127 | std::string Polygon_with_interior_rings(Rcpp::List geom_lst, int i, bool verbose); 128 | RcppExport SEXP _geojsonR_Polygon_with_interior_rings(SEXP geom_lstSEXP, SEXP iSEXP, SEXP verboseSEXP) { 129 | BEGIN_RCPP 130 | Rcpp::RObject rcpp_result_gen; 131 | Rcpp::RNGScope rcpp_rngScope_gen; 132 | Rcpp::traits::input_parameter< Rcpp::List >::type geom_lst(geom_lstSEXP); 133 | Rcpp::traits::input_parameter< int >::type i(iSEXP); 134 | Rcpp::traits::input_parameter< bool >::type verbose(verboseSEXP); 135 | rcpp_result_gen = Rcpp::wrap(Polygon_with_interior_rings(geom_lst, i, verbose)); 136 | return rcpp_result_gen; 137 | END_RCPP 138 | } 139 | // SAVE_R_list_Features_2_FeatureCollection 140 | std::string SAVE_R_list_Features_2_FeatureCollection(Rcpp::List x, std::string path_to_file, bool verbose); 141 | RcppExport SEXP _geojsonR_SAVE_R_list_Features_2_FeatureCollection(SEXP xSEXP, SEXP path_to_fileSEXP, SEXP verboseSEXP) { 142 | BEGIN_RCPP 143 | Rcpp::RObject rcpp_result_gen; 144 | Rcpp::RNGScope rcpp_rngScope_gen; 145 | Rcpp::traits::input_parameter< Rcpp::List >::type x(xSEXP); 146 | Rcpp::traits::input_parameter< std::string >::type path_to_file(path_to_fileSEXP); 147 | Rcpp::traits::input_parameter< bool >::type verbose(verboseSEXP); 148 | rcpp_result_gen = Rcpp::wrap(SAVE_R_list_Features_2_FeatureCollection(x, path_to_file, verbose)); 149 | return rcpp_result_gen; 150 | END_RCPP 151 | } 152 | // export_To_GeoJson 153 | Rcpp::List export_To_GeoJson(std::string geometry_object, std::vector data_POINTS, std::vector> data_ARRAYS, std::vector>> data_ARRAY_ARRAYS, std::vector>>> data_POLYGON_ARRAYS, bool stringify); 154 | RcppExport SEXP _geojsonR_export_To_GeoJson(SEXP geometry_objectSEXP, SEXP data_POINTSSEXP, SEXP data_ARRAYSSEXP, SEXP data_ARRAY_ARRAYSSEXP, SEXP data_POLYGON_ARRAYSSEXP, SEXP stringifySEXP) { 155 | BEGIN_RCPP 156 | Rcpp::RObject rcpp_result_gen; 157 | Rcpp::RNGScope rcpp_rngScope_gen; 158 | Rcpp::traits::input_parameter< std::string >::type geometry_object(geometry_objectSEXP); 159 | Rcpp::traits::input_parameter< std::vector >::type data_POINTS(data_POINTSSEXP); 160 | Rcpp::traits::input_parameter< std::vector> >::type data_ARRAYS(data_ARRAYSSEXP); 161 | Rcpp::traits::input_parameter< std::vector>> >::type data_ARRAY_ARRAYS(data_ARRAY_ARRAYSSEXP); 162 | Rcpp::traits::input_parameter< std::vector>>> >::type data_POLYGON_ARRAYS(data_POLYGON_ARRAYSSEXP); 163 | Rcpp::traits::input_parameter< bool >::type stringify(stringifySEXP); 164 | rcpp_result_gen = Rcpp::wrap(export_To_GeoJson(geometry_object, data_POINTS, data_ARRAYS, data_ARRAY_ARRAYS, data_POLYGON_ARRAYS, stringify)); 165 | return rcpp_result_gen; 166 | END_RCPP 167 | } 168 | // Geom_Collection 169 | Rcpp::List Geom_Collection(std::vector geometry_object_names, Rcpp::List geometry_objects, bool stringify); 170 | RcppExport SEXP _geojsonR_Geom_Collection(SEXP geometry_object_namesSEXP, SEXP geometry_objectsSEXP, SEXP stringifySEXP) { 171 | BEGIN_RCPP 172 | Rcpp::RObject rcpp_result_gen; 173 | Rcpp::RNGScope rcpp_rngScope_gen; 174 | Rcpp::traits::input_parameter< std::vector >::type geometry_object_names(geometry_object_namesSEXP); 175 | Rcpp::traits::input_parameter< Rcpp::List >::type geometry_objects(geometry_objectsSEXP); 176 | Rcpp::traits::input_parameter< bool >::type stringify(stringifySEXP); 177 | rcpp_result_gen = Rcpp::wrap(Geom_Collection(geometry_object_names, geometry_objects, stringify)); 178 | return rcpp_result_gen; 179 | END_RCPP 180 | } 181 | // Feature_Obj 182 | Rcpp::List Feature_Obj(std::vector geometry_object_names, Rcpp::List geometry_objects, bool stringify); 183 | RcppExport SEXP _geojsonR_Feature_Obj(SEXP geometry_object_namesSEXP, SEXP geometry_objectsSEXP, SEXP stringifySEXP) { 184 | BEGIN_RCPP 185 | Rcpp::RObject rcpp_result_gen; 186 | Rcpp::RNGScope rcpp_rngScope_gen; 187 | Rcpp::traits::input_parameter< std::vector >::type geometry_object_names(geometry_object_namesSEXP); 188 | Rcpp::traits::input_parameter< Rcpp::List >::type geometry_objects(geometry_objectsSEXP); 189 | Rcpp::traits::input_parameter< bool >::type stringify(stringifySEXP); 190 | rcpp_result_gen = Rcpp::wrap(Feature_Obj(geometry_object_names, geometry_objects, stringify)); 191 | return rcpp_result_gen; 192 | END_RCPP 193 | } 194 | // Feature_collection_Obj 195 | Rcpp::List Feature_collection_Obj(std::vector geometry_object_names, Rcpp::List geometry_objects, bool stringify); 196 | RcppExport SEXP _geojsonR_Feature_collection_Obj(SEXP geometry_object_namesSEXP, SEXP geometry_objectsSEXP, SEXP stringifySEXP) { 197 | BEGIN_RCPP 198 | Rcpp::RObject rcpp_result_gen; 199 | Rcpp::RNGScope rcpp_rngScope_gen; 200 | Rcpp::traits::input_parameter< std::vector >::type geometry_object_names(geometry_object_namesSEXP); 201 | Rcpp::traits::input_parameter< Rcpp::List >::type geometry_objects(geometry_objectsSEXP); 202 | Rcpp::traits::input_parameter< bool >::type stringify(stringifySEXP); 203 | rcpp_result_gen = Rcpp::wrap(Feature_collection_Obj(geometry_object_names, geometry_objects, stringify)); 204 | return rcpp_result_gen; 205 | END_RCPP 206 | } 207 | -------------------------------------------------------------------------------- /src/TO_geojson.cpp: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Copyright (C) 2017 Lampros Mouselimis 4 | * 5 | * @file TO_geojson.cpp 6 | * 7 | * @author Lampros Mouselimis 8 | * 9 | * @date February - March 2017 10 | * 11 | * @Notes: converts data to a GeoJson object 12 | * 13 | * @last_modified: March 2017 14 | * 15 | **/ 16 | 17 | 18 | # include 19 | // [[Rcpp::depends("RcppArmadillo")]] 20 | // [[Rcpp::plugins(cpp11)]] 21 | 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | #include "json11.h" 32 | 33 | 34 | 35 | // Classes to build GeoJson-Geometries 36 | // 37 | 38 | template 39 | class GeoJson_Geometries { 40 | 41 | public: 42 | 43 | GeoJson_Geometries() { } 44 | 45 | 46 | // inner geometry object (json11) 47 | // 48 | 49 | json11::Json Inner_GeoJson(std::string geometry_object, T data) { 50 | 51 | json11::Json::array OBJ; 52 | 53 | for (unsigned int i = 0; i < data.size(); i++) { 54 | 55 | OBJ.push_back(data[i]); 56 | } 57 | 58 | json11::Json geo_json = json11::Json::object { 59 | 60 | { "type", geometry_object }, 61 | 62 | { "coordinates", OBJ }, 63 | }; 64 | 65 | return geo_json; 66 | } 67 | 68 | 69 | // geometry-object : "Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon" 70 | // 71 | 72 | Rcpp::List To_Geom_Obj(std::string geometry_object, T data, bool stringify = false) { 73 | 74 | Rcpp::List RES; 75 | 76 | if (stringify) { 77 | 78 | json11::Json geo_json = Inner_GeoJson(geometry_object, data); 79 | 80 | std::string json_str = geo_json.dump(); 81 | 82 | RES["json_dump"] = json_str; 83 | } 84 | 85 | RES["type"] = geometry_object; 86 | 87 | RES["coordinates"] = data; 88 | 89 | return RES; 90 | } 91 | 92 | ~GeoJson_Geometries() { } 93 | }; 94 | 95 | 96 | 97 | 98 | // Class to build : 'Geometry-Collection', 'Feature', and 'Feature-Collection' 99 | // 100 | 101 | 102 | class GeoJson_Collections { 103 | 104 | public: 105 | 106 | // array of geometry-objects 107 | // 108 | 109 | json11::Json::array array_geometry_collection(std::vector geometry_object_names, Rcpp::List geometry_objects) { 110 | 111 | json11::Json::array array_geometries; 112 | 113 | for (unsigned int i = 0; i < geometry_object_names.size(); i++) { 114 | 115 | json11::Json geo_json; 116 | 117 | if (geometry_object_names[i] == "Point") { 118 | 119 | GeoJson_Geometries> gjg; 120 | 121 | std::vector tmp_dat = geometry_objects[i]; 122 | 123 | geo_json = gjg.Inner_GeoJson(geometry_object_names[i], tmp_dat); 124 | } 125 | 126 | else if (geometry_object_names[i] == "MultiPoint" || geometry_object_names[i] == "LineString") { 127 | 128 | GeoJson_Geometries>> gjg; 129 | 130 | std::vector> tmp_dat = geometry_objects[i]; 131 | 132 | geo_json = gjg.Inner_GeoJson(geometry_object_names[i], tmp_dat); 133 | } 134 | 135 | else if (geometry_object_names[i] == "MultiLineString" || geometry_object_names[i] == "Polygon") { 136 | 137 | GeoJson_Geometries>>> gjg; 138 | 139 | std::vector>> tmp_dat = geometry_objects[i]; 140 | 141 | geo_json = gjg.Inner_GeoJson(geometry_object_names[i], tmp_dat); 142 | } 143 | 144 | else if (geometry_object_names[i] == "MultiPolygon") { 145 | 146 | GeoJson_Geometries>>>> gjg; 147 | 148 | std::vector>>> tmp_dat = geometry_objects[i]; 149 | 150 | geo_json = gjg.Inner_GeoJson(geometry_object_names[i], tmp_dat); 151 | } 152 | 153 | else { 154 | 155 | Rcpp::stop("invalid GeoJson geometry object --> array_geometry_collection() function"); 156 | } 157 | 158 | array_geometries.push_back(geo_json); 159 | } 160 | 161 | return array_geometries; 162 | } 163 | 164 | 165 | // 'GeometryCollection' object 166 | // 167 | 168 | Rcpp::List geometry_collection(std::vector geometry_object_names, Rcpp::List geometry_objects, bool stringify = false) { 169 | 170 | Rcpp::List RES; 171 | 172 | std::string tmp_nam = "GeometryCollection"; 173 | 174 | if (stringify) { 175 | 176 | json11::Json::array ARRAY = array_geometry_collection(geometry_object_names, geometry_objects); 177 | 178 | json11::Json geom_OBJECTS = json11::Json::object { 179 | 180 | { "type", tmp_nam }, 181 | 182 | { "geometries", ARRAY }, 183 | 184 | }; 185 | 186 | std::string json_str = geom_OBJECTS.dump(); 187 | 188 | RES["json_dump"] = json_str; 189 | } 190 | 191 | RES["type"] = tmp_nam; 192 | 193 | RES["geometries"] = geometry_objects; 194 | 195 | return RES; 196 | } 197 | 198 | 199 | // recursive function for the 'properties' member of the 'Feature' object [ if stringify = TRUE ] 200 | // For, the 'TYPEOF()', 'LENGTH', 'REALSXP', 'LGLSXP etc. SEE : 201 | // http://adv-r.had.co.nz/C-interface.html, https://github.com/hadley/pryr/blob/master/src/typename.cpp, 202 | // https://github.com/hadley/r-internals/blob/master/vectors.md, http://gallery.rcpp.org/articles/rcpp-wrap-and-recurse/ 203 | // 204 | 205 | json11::Json typeof_item(Rcpp::List rec_prop) { 206 | 207 | json11::Json::object properties_OBJ; 208 | 209 | std::vector prop_nams = rec_prop.attr("names"); 210 | 211 | unsigned int REC_SIZE = rec_prop.size(); 212 | 213 | for (unsigned int f = 0; f < REC_SIZE; f++) { 214 | 215 | std::string tmp_nam = prop_nams[f]; 216 | 217 | int length_item = LENGTH(rec_prop[f]); // distinction between 'float' and 'vector of floats' using LENGTH() 218 | 219 | if (length_item == 1) { 220 | 221 | if (TYPEOF(rec_prop[f]) == REALSXP) { 222 | 223 | double tmp_dbl = Rcpp::as(rec_prop[f]); 224 | 225 | properties_OBJ[tmp_nam] = json11::Json(tmp_dbl);} 226 | 227 | else if (TYPEOF(rec_prop[f]) == INTSXP) { 228 | 229 | int tmp_dbl_int = Rcpp::as(rec_prop[f]); 230 | 231 | properties_OBJ[tmp_nam] = json11::Json(tmp_dbl_int);} 232 | 233 | else if (TYPEOF(rec_prop[f]) == LGLSXP) { 234 | 235 | bool tmp_bool = rec_prop[f]; 236 | 237 | properties_OBJ[tmp_nam] = json11::Json(tmp_bool);} 238 | 239 | else if (TYPEOF(rec_prop[f]) == STRSXP) { 240 | 241 | std::string tmp_str = Rcpp::as(rec_prop[f]); 242 | 243 | properties_OBJ[tmp_nam] = json11::Json(tmp_str);} 244 | 245 | else if (TYPEOF(rec_prop[f]) == NILSXP) { 246 | 247 | std::string tmp_str = "null"; 248 | 249 | properties_OBJ[tmp_nam] = json11::Json(tmp_str);} 250 | 251 | else if (TYPEOF(rec_prop[f]) == VECSXP) { 252 | 253 | Rcpp::List tmp_lst = rec_prop[f]; 254 | 255 | properties_OBJ[tmp_nam] = typeof_item(tmp_lst);} 256 | 257 | else { 258 | 259 | Rcpp::stop("invalid Json object of length == 1 --> typeof_item() function"); 260 | } 261 | } 262 | 263 | else { 264 | 265 | if (TYPEOF(rec_prop[f]) == REALSXP) { 266 | 267 | std::vector arr_vec = Rcpp::as>(rec_prop[f]); 268 | 269 | properties_OBJ[tmp_nam] = json11::Json(arr_vec);} 270 | 271 | else if (TYPEOF(rec_prop[f]) == INTSXP) { 272 | 273 | std::vector arr_vec_int = Rcpp::as>(rec_prop[f]); 274 | 275 | properties_OBJ[tmp_nam] = json11::Json(arr_vec_int);} 276 | 277 | else if (TYPEOF(rec_prop[f]) == VECSXP) { 278 | 279 | Rcpp::List tmp_lst = rec_prop[f]; 280 | 281 | properties_OBJ[tmp_nam] = typeof_item(tmp_lst);} 282 | 283 | else { 284 | 285 | Rcpp::stop("invalid Json object of length > 1 --> typeof_item() function"); 286 | } 287 | } 288 | } 289 | 290 | return properties_OBJ; 291 | } 292 | 293 | 294 | // inner 'json11::Json::object' function for the 'properties' member of the 'Feature' object [ if stringify = TRUE ] 295 | // 296 | 297 | json11::Json::object inner_Feature(std::vector geometry_object_names, Rcpp::List geometry_objects, std::string Feature_name) { 298 | 299 | json11::Json::object feature_OBJ; // build the Json::object incrementally 300 | 301 | feature_OBJ["type"] = Feature_name; 302 | 303 | for (unsigned int i = 0; i < geometry_object_names.size(); i++) { 304 | 305 | if (geometry_object_names[i] == "id") { 306 | 307 | if (TYPEOF(geometry_objects[i]) == STRSXP) { 308 | 309 | std::string tmp_id = Rcpp::as(geometry_objects["id"]); 310 | 311 | feature_OBJ["id"] = json11::Json(tmp_id);} 312 | 313 | else if (TYPEOF(geometry_objects[i]) == REALSXP) { 314 | 315 | double tmp_id = Rcpp::as(geometry_objects["id"]); 316 | 317 | feature_OBJ["id"] = json11::Json(tmp_id);} 318 | 319 | else { 320 | 321 | Rcpp::stop("the 'id' member should be either a character string or a numeric value --> inner_Feature() function"); 322 | } 323 | } 324 | 325 | else if (geometry_object_names[i] == "bbox") { 326 | 327 | std::vector bbox_vec = geometry_objects["bbox"]; 328 | 329 | feature_OBJ["bbox"] = json11::Json(bbox_vec);} 330 | 331 | else if (geometry_object_names[i] == "geometry") { 332 | 333 | Rcpp::List tmp_lst = geometry_objects[i]; 334 | 335 | std::vector tmp_lst_nams = tmp_lst.attr("names"); 336 | 337 | json11::Json::array tmp_lst_array = array_geometry_collection(tmp_lst_nams, geometry_objects[i]); // std::vector, Rcpp::List as input 338 | 339 | feature_OBJ["geometry"] = json11::Json(tmp_lst_array[0]);} // extract the object from the array [ first array element ] 340 | 341 | else if (geometry_object_names[i] == "properties") { 342 | 343 | json11::Json::object properties_OBJ; // initialize second json-object for properties; 344 | 345 | Rcpp::List tmp_prop = geometry_objects[i]; 346 | 347 | if (tmp_prop.size() == 0) { 348 | 349 | feature_OBJ["properties"] = json11::Json(properties_OBJ);} // empty object if Rcpp::List is empty 350 | 351 | else { 352 | 353 | feature_OBJ["properties"] = typeof_item(tmp_prop); // recursive function 354 | } 355 | } 356 | 357 | else { 358 | 359 | Rcpp::stop("invalid member of the Feature geometry object --> inner_Feature() function"); 360 | } 361 | } 362 | 363 | return feature_OBJ; 364 | } 365 | 366 | 367 | // 'Feature' geometry object 368 | // 369 | 370 | Rcpp::List feature_OBJECT(std::vector geometry_object_names, Rcpp::List geometry_objects, bool stringify = false) { 371 | 372 | Rcpp::List RES = geometry_objects; 373 | 374 | std::string tmp_nam = "Feature"; 375 | 376 | if (stringify) { 377 | 378 | json11::Json::object feature_OBJ = inner_Feature(geometry_object_names, geometry_objects, tmp_nam); 379 | 380 | json11::Json res_json = feature_OBJ; 381 | 382 | std::string json_str = res_json.dump(); 383 | 384 | RES["json_dump"] = json_str; 385 | } 386 | 387 | RES["type"] = tmp_nam; 388 | 389 | return RES; 390 | } 391 | 392 | 393 | // 'FeatureCollection' geometry object 394 | // 395 | 396 | Rcpp::List feature_collection(std::vector geometry_object_names, Rcpp::List geometry_objects, bool stringify = false) { 397 | 398 | Rcpp::List RES = geometry_objects; 399 | 400 | std::string feat_col_nam = "FeatureCollection"; 401 | 402 | RES["type"] = feat_col_nam; 403 | 404 | if (stringify) { 405 | 406 | json11::Json::object feature_col_OBJ; 407 | 408 | feature_col_OBJ["type"] = feat_col_nam; 409 | 410 | for (unsigned int i = 0; i < geometry_object_names.size(); i++) { 411 | 412 | if (geometry_object_names[i] == "bbox") { 413 | 414 | std::vector bbox_vec = geometry_objects["bbox"]; 415 | 416 | feature_col_OBJ["bbox"] = json11::Json(bbox_vec);} 417 | 418 | else if (geometry_object_names[i] == "features") { 419 | 420 | json11::Json::array multiple_features_OBJ; // initialize second json-object for multiple-feature-objects; 421 | 422 | Rcpp::List tmp_feat = geometry_objects[i]; 423 | 424 | if (tmp_feat.size() == 0) { 425 | 426 | feature_col_OBJ["features"] = json11::Json(multiple_features_OBJ);} // empty object if Rcpp::List is empty 427 | 428 | else { 429 | 430 | std::vector outer_lst_nams = tmp_feat.attr("names"); 431 | 432 | for (unsigned int j = 0; j < outer_lst_nams.size(); j++) { 433 | 434 | Rcpp::List inner_feat_lst = tmp_feat[j]; 435 | 436 | std::vector inner_lst_nams = inner_feat_lst.attr("names"); 437 | 438 | std::string tmp_inner_nam = outer_lst_nams[j]; 439 | 440 | json11::Json::object inner_loop_json11 = inner_Feature(inner_lst_nams, inner_feat_lst, tmp_inner_nam); 441 | 442 | multiple_features_OBJ.push_back(inner_loop_json11); 443 | } 444 | } 445 | 446 | feature_col_OBJ["features"] = multiple_features_OBJ; 447 | } 448 | 449 | else { 450 | 451 | Rcpp::stop("invalid object for the member 'features' of the 'FeatureCollection' object --> feature_collection() function"); 452 | } 453 | } 454 | 455 | json11::Json inner_res_json = feature_col_OBJ; 456 | 457 | std::string json_str = inner_res_json.dump(); 458 | 459 | RES["json_dump"] = json_str; 460 | } 461 | 462 | return RES; 463 | } 464 | 465 | }; 466 | 467 | 468 | 469 | 470 | //---------------------------------- 471 | // TO-GeoJson functions [ exported ] 472 | //---------------------------------- 473 | 474 | 475 | // Returns one of the following geometry-objects : "Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon" 476 | // either the 'vector' OR the 'nested-vector' of the objects must be an empty object [ empty = numeric(0) ] 477 | // 478 | 479 | // [[Rcpp::export]] 480 | Rcpp::List export_To_GeoJson(std::string geometry_object, std::vector data_POINTS, std::vector> data_ARRAYS, std::vector>> data_ARRAY_ARRAYS, 481 | 482 | std::vector>>> data_POLYGON_ARRAYS, bool stringify = false) { 483 | 484 | 485 | if (geometry_object == "Point") { 486 | 487 | GeoJson_Geometries> tgj; 488 | 489 | return tgj.To_Geom_Obj(geometry_object, data_POINTS, stringify); 490 | } 491 | 492 | else if (geometry_object == "MultiPoint" || geometry_object == "LineString") { 493 | 494 | GeoJson_Geometries>> tgj; 495 | 496 | return tgj.To_Geom_Obj(geometry_object, data_ARRAYS, stringify);} 497 | 498 | else if (geometry_object == "MultiLineString" || geometry_object == "Polygon") { 499 | 500 | GeoJson_Geometries>>> tgj; 501 | 502 | return tgj.To_Geom_Obj(geometry_object, data_ARRAY_ARRAYS, stringify);} 503 | 504 | else if (geometry_object == "MultiPolygon") { 505 | 506 | GeoJson_Geometries>>>> tgj; 507 | 508 | return tgj.To_Geom_Obj(geometry_object, data_POLYGON_ARRAYS, stringify);} 509 | 510 | else { 511 | 512 | Rcpp::stop("invalid geometry object --> export_To_GeoJson() function"); 513 | } 514 | } 515 | 516 | 517 | 518 | // Geometry-collection object 519 | // 520 | 521 | // [[Rcpp::export]] 522 | Rcpp::List Geom_Collection(std::vector geometry_object_names, Rcpp::List geometry_objects, bool stringify = false) { 523 | 524 | GeoJson_Collections gjc_geometry; 525 | 526 | return gjc_geometry.geometry_collection(geometry_object_names, geometry_objects, stringify); 527 | } 528 | 529 | 530 | 531 | // Feature object 532 | // 533 | 534 | // [[Rcpp::export]] 535 | Rcpp::List Feature_Obj(std::vector geometry_object_names, Rcpp::List geometry_objects, bool stringify = false) { 536 | 537 | GeoJson_Collections gjc_feature; 538 | 539 | return gjc_feature.feature_OBJECT(geometry_object_names, geometry_objects, stringify); 540 | } 541 | 542 | 543 | 544 | // FeatureCollection object 545 | // 546 | 547 | // [[Rcpp::export]] 548 | Rcpp::List Feature_collection_Obj(std::vector geometry_object_names, Rcpp::List geometry_objects, bool stringify = false) { 549 | 550 | GeoJson_Collections gjc_feature_collection; 551 | 552 | return gjc_feature_collection.feature_collection(geometry_object_names, geometry_objects, stringify); 553 | } 554 | 555 | 556 | -------------------------------------------------------------------------------- /src/init.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // for NULL 4 | #include 5 | 6 | /* FIXME: 7 | Check these declarations against the C/Fortran source code. 8 | */ 9 | 10 | /* .Call calls */ 11 | extern SEXP _geojsonR_DATA_TYPE(SEXP); 12 | extern SEXP _geojsonR_dump_geojson(SEXP); 13 | extern SEXP _geojsonR_export_From_geojson(SEXP, SEXP, SEXP, SEXP); 14 | extern SEXP _geojsonR_export_From_geojson_schema(SEXP, SEXP, SEXP, SEXP); 15 | extern SEXP _geojsonR_export_From_JSON(SEXP); 16 | extern SEXP _geojsonR_export_To_GeoJson(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); 17 | extern SEXP _geojsonR_Feature_collection_Obj(SEXP, SEXP, SEXP); 18 | extern SEXP _geojsonR_Feature_Obj(SEXP, SEXP, SEXP); 19 | extern SEXP _geojsonR_Features_TO_Collection(SEXP, SEXP, SEXP); 20 | extern SEXP _geojsonR_Geom_Collection(SEXP, SEXP, SEXP); 21 | extern SEXP _geojsonR_inner_coords(SEXP, SEXP, SEXP); 22 | extern SEXP _geojsonR_list_files(SEXP, SEXP); 23 | extern SEXP _geojsonR_merge_json(SEXP, SEXP, SEXP, SEXP); 24 | extern SEXP _geojsonR_Polygon_with_interior_rings(SEXP, SEXP, SEXP); 25 | extern SEXP _geojsonR_SAVE_R_list_Features_2_FeatureCollection(SEXP, SEXP, SEXP); 26 | 27 | static const R_CallMethodDef CallEntries[] = { 28 | {"_geojsonR_DATA_TYPE", (DL_FUNC) &_geojsonR_DATA_TYPE, 1}, 29 | {"_geojsonR_dump_geojson", (DL_FUNC) &_geojsonR_dump_geojson, 1}, 30 | {"_geojsonR_export_From_geojson", (DL_FUNC) &_geojsonR_export_From_geojson, 4}, 31 | {"_geojsonR_export_From_geojson_schema", (DL_FUNC) &_geojsonR_export_From_geojson_schema, 4}, 32 | {"_geojsonR_export_From_JSON", (DL_FUNC) &_geojsonR_export_From_JSON, 1}, 33 | {"_geojsonR_export_To_GeoJson", (DL_FUNC) &_geojsonR_export_To_GeoJson, 6}, 34 | {"_geojsonR_Feature_collection_Obj", (DL_FUNC) &_geojsonR_Feature_collection_Obj, 3}, 35 | {"_geojsonR_Feature_Obj", (DL_FUNC) &_geojsonR_Feature_Obj, 3}, 36 | {"_geojsonR_Features_TO_Collection", (DL_FUNC) &_geojsonR_Features_TO_Collection, 3}, 37 | {"_geojsonR_Geom_Collection", (DL_FUNC) &_geojsonR_Geom_Collection, 3}, 38 | {"_geojsonR_inner_coords", (DL_FUNC) &_geojsonR_inner_coords, 3}, 39 | {"_geojsonR_list_files", (DL_FUNC) &_geojsonR_list_files, 2}, 40 | {"_geojsonR_merge_json", (DL_FUNC) &_geojsonR_merge_json, 4}, 41 | {"_geojsonR_Polygon_with_interior_rings", (DL_FUNC) &_geojsonR_Polygon_with_interior_rings, 3}, 42 | {"_geojsonR_SAVE_R_list_Features_2_FeatureCollection", (DL_FUNC) &_geojsonR_SAVE_R_list_Features_2_FeatureCollection, 3}, 43 | {NULL, NULL, 0} 44 | }; 45 | 46 | void R_init_geojsonR(DllInfo *dll) 47 | { 48 | R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); 49 | R_useDynamicSymbols(dll, FALSE); 50 | } 51 | -------------------------------------------------------------------------------- /src/json11.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2013 Dropbox, Inc. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to deal 5 | * in the Software without restriction, including without limitation the rights 6 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | * copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | */ 21 | 22 | #include "json11.h" 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | namespace json11 { 31 | 32 | static const int max_depth = 200; 33 | 34 | using std::string; 35 | using std::vector; 36 | using std::map; 37 | using std::make_shared; 38 | using std::initializer_list; 39 | 40 | /* Helper for representing null - just a do-nothing struct, plus comparison 41 | * operators so the helpers in JsonValue work. We can't use nullptr_t because 42 | * it may not be orderable. 43 | */ 44 | struct NullStruct { 45 | bool operator==(NullStruct) const { return true; } 46 | bool operator<(NullStruct) const { return false; } 47 | }; 48 | 49 | /* * * * * * * * * * * * * * * * * * * * 50 | * Serialization 51 | */ 52 | 53 | static void dump(NullStruct, string &out) { 54 | out += "null"; 55 | } 56 | 57 | static void dump(double value, string &out) { 58 | if (std::isfinite(value)) { 59 | char buf[32]; 60 | snprintf(buf, sizeof buf, "%.17g", value); 61 | out += buf; 62 | } else { 63 | out += "null"; 64 | } 65 | } 66 | 67 | static void dump(int value, string &out) { 68 | char buf[32]; 69 | snprintf(buf, sizeof buf, "%d", value); 70 | out += buf; 71 | } 72 | 73 | static void dump(bool value, string &out) { 74 | out += value ? "true" : "false"; 75 | } 76 | 77 | static void dump(const string &value, string &out) { 78 | out += '"'; 79 | for (size_t i = 0; i < value.length(); i++) { 80 | const char ch = value[i]; 81 | if (ch == '\\') { 82 | out += "\\\\"; 83 | } else if (ch == '"') { 84 | out += "\\\""; 85 | } else if (ch == '\b') { 86 | out += "\\b"; 87 | } else if (ch == '\f') { 88 | out += "\\f"; 89 | } else if (ch == '\n') { 90 | out += "\\n"; 91 | } else if (ch == '\r') { 92 | out += "\\r"; 93 | } else if (ch == '\t') { 94 | out += "\\t"; 95 | } else if (static_cast(ch) <= 0x1f) { 96 | char buf[8]; 97 | snprintf(buf, sizeof buf, "\\u%04x", ch); 98 | out += buf; 99 | } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 100 | && static_cast(value[i+2]) == 0xa8) { 101 | out += "\\u2028"; 102 | i += 2; 103 | } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 104 | && static_cast(value[i+2]) == 0xa9) { 105 | out += "\\u2029"; 106 | i += 2; 107 | } else { 108 | out += ch; 109 | } 110 | } 111 | out += '"'; 112 | } 113 | 114 | static void dump(const Json::array &values, string &out) { 115 | bool first = true; 116 | out += "["; 117 | for (const auto &value : values) { 118 | if (!first) 119 | out += ", "; 120 | value.dump(out); 121 | first = false; 122 | } 123 | out += "]"; 124 | } 125 | 126 | static void dump(const Json::object &values, string &out) { 127 | bool first = true; 128 | out += "{"; 129 | for (const auto &kv : values) { 130 | if (!first) 131 | out += ", "; 132 | dump(kv.first, out); 133 | out += ": "; 134 | kv.second.dump(out); 135 | first = false; 136 | } 137 | out += "}"; 138 | } 139 | 140 | void Json::dump(string &out) const { 141 | m_ptr->dump(out); 142 | } 143 | 144 | /* * * * * * * * * * * * * * * * * * * * 145 | * Value wrappers 146 | */ 147 | 148 | template 149 | class Value : public JsonValue { 150 | protected: 151 | 152 | // Constructors 153 | explicit Value(const T &value) : m_value(value) {} 154 | explicit Value(T &&value) : m_value(std::move(value)) {} 155 | 156 | // Get type tag 157 | Json::Type type() const override { 158 | return tag; 159 | } 160 | 161 | // Comparisons 162 | bool equals(const JsonValue * other) const override { 163 | return m_value == static_cast *>(other)->m_value; 164 | } 165 | bool less(const JsonValue * other) const override { 166 | return m_value < static_cast *>(other)->m_value; 167 | } 168 | 169 | const T m_value; 170 | void dump(string &out) const override { json11::dump(m_value, out); } 171 | }; 172 | 173 | class JsonDouble final : public Value { 174 | double number_value() const override { return m_value; } 175 | int int_value() const override { return static_cast(m_value); } 176 | bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } 177 | bool less(const JsonValue * other) const override { return m_value < other->number_value(); } 178 | public: 179 | explicit JsonDouble(double value) : Value(value) {} 180 | }; 181 | 182 | class JsonInt final : public Value { 183 | double number_value() const override { return m_value; } 184 | int int_value() const override { return m_value; } 185 | bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } 186 | bool less(const JsonValue * other) const override { return m_value < other->number_value(); } 187 | public: 188 | explicit JsonInt(int value) : Value(value) {} 189 | }; 190 | 191 | class JsonBoolean final : public Value { 192 | bool bool_value() const override { return m_value; } 193 | public: 194 | explicit JsonBoolean(bool value) : Value(value) {} 195 | }; 196 | 197 | class JsonString final : public Value { 198 | const string &string_value() const override { return m_value; } 199 | public: 200 | explicit JsonString(const string &value) : Value(value) {} 201 | explicit JsonString(string &&value) : Value(std::move(value)) {} 202 | }; 203 | 204 | class JsonArray final : public Value { 205 | const Json::array &array_items() const override { return m_value; } 206 | const Json & operator[](size_t i) const override; 207 | public: 208 | explicit JsonArray(const Json::array &value) : Value(value) {} 209 | explicit JsonArray(Json::array &&value) : Value(std::move(value)) {} 210 | }; 211 | 212 | class JsonObject final : public Value { 213 | const Json::object &object_items() const override { return m_value; } 214 | const Json & operator[](const string &key) const override; 215 | public: 216 | explicit JsonObject(const Json::object &value) : Value(value) {} 217 | explicit JsonObject(Json::object &&value) : Value(std::move(value)) {} 218 | }; 219 | 220 | class JsonNull final : public Value { 221 | public: 222 | JsonNull() : Value({}) {} 223 | }; 224 | 225 | /* * * * * * * * * * * * * * * * * * * * 226 | * Static globals - static-init-safe 227 | */ 228 | struct Statics { 229 | const std::shared_ptr null = make_shared(); 230 | const std::shared_ptr t = make_shared(true); 231 | const std::shared_ptr f = make_shared(false); 232 | const string empty_string; 233 | const vector empty_vector; 234 | const map empty_map; 235 | Statics() {} 236 | }; 237 | 238 | static const Statics & statics() { 239 | static const Statics s {}; 240 | return s; 241 | } 242 | 243 | static const Json & static_null() { 244 | // This has to be separate, not in Statics, because Json() accesses statics().null. 245 | static const Json json_null; 246 | return json_null; 247 | } 248 | 249 | /* * * * * * * * * * * * * * * * * * * * 250 | * Constructors 251 | */ 252 | 253 | Json::Json() noexcept : m_ptr(statics().null) {} 254 | Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {} 255 | Json::Json(double value) : m_ptr(make_shared(value)) {} 256 | Json::Json(int value) : m_ptr(make_shared(value)) {} 257 | Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {} 258 | Json::Json(const string &value) : m_ptr(make_shared(value)) {} 259 | Json::Json(string &&value) : m_ptr(make_shared(std::move(value))) {} 260 | Json::Json(const char * value) : m_ptr(make_shared(value)) {} 261 | Json::Json(const Json::array &values) : m_ptr(make_shared(values)) {} 262 | Json::Json(Json::array &&values) : m_ptr(make_shared(std::move(values))) {} 263 | Json::Json(const Json::object &values) : m_ptr(make_shared(values)) {} 264 | Json::Json(Json::object &&values) : m_ptr(make_shared(std::move(values))) {} 265 | 266 | /* * * * * * * * * * * * * * * * * * * * 267 | * Accessors 268 | */ 269 | 270 | Json::Type Json::type() const { return m_ptr->type(); } 271 | double Json::number_value() const { return m_ptr->number_value(); } 272 | int Json::int_value() const { return m_ptr->int_value(); } 273 | bool Json::bool_value() const { return m_ptr->bool_value(); } 274 | const string & Json::string_value() const { return m_ptr->string_value(); } 275 | const vector & Json::array_items() const { return m_ptr->array_items(); } 276 | const map & Json::object_items() const { return m_ptr->object_items(); } 277 | const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; } 278 | const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; } 279 | 280 | double JsonValue::number_value() const { return 0; } 281 | int JsonValue::int_value() const { return 0; } 282 | bool JsonValue::bool_value() const { return false; } 283 | const string & JsonValue::string_value() const { return statics().empty_string; } 284 | const vector & JsonValue::array_items() const { return statics().empty_vector; } 285 | const map & JsonValue::object_items() const { return statics().empty_map; } 286 | const Json & JsonValue::operator[] (size_t) const { return static_null(); } 287 | const Json & JsonValue::operator[] (const string &) const { return static_null(); } 288 | 289 | const Json & JsonObject::operator[] (const string &key) const { 290 | auto iter = m_value.find(key); 291 | return (iter == m_value.end()) ? static_null() : iter->second; 292 | } 293 | const Json & JsonArray::operator[] (size_t i) const { 294 | if (i >= m_value.size()) return static_null(); 295 | else return m_value[i]; 296 | } 297 | 298 | /* * * * * * * * * * * * * * * * * * * * 299 | * Comparison 300 | */ 301 | 302 | bool Json::operator== (const Json &other) const { 303 | if (m_ptr->type() != other.m_ptr->type()) 304 | return false; 305 | 306 | return m_ptr->equals(other.m_ptr.get()); 307 | } 308 | 309 | bool Json::operator< (const Json &other) const { 310 | if (m_ptr->type() != other.m_ptr->type()) 311 | return m_ptr->type() < other.m_ptr->type(); 312 | 313 | return m_ptr->less(other.m_ptr.get()); 314 | } 315 | 316 | /* * * * * * * * * * * * * * * * * * * * 317 | * Parsing 318 | */ 319 | 320 | /* esc(c) 321 | * 322 | * Format char c suitable for printing in an error message. 323 | */ 324 | static inline string esc(char c) { 325 | char buf[12]; 326 | if (static_cast(c) >= 0x20 && static_cast(c) <= 0x7f) { 327 | snprintf(buf, sizeof buf, "'%c' (%d)", c, c); 328 | } else { 329 | snprintf(buf, sizeof buf, "(%d)", c); 330 | } 331 | return string(buf); 332 | } 333 | 334 | static inline bool in_range(long x, long lower, long upper) { 335 | return (x >= lower && x <= upper); 336 | } 337 | 338 | namespace { 339 | /* JsonParser 340 | * 341 | * Object that tracks all state of an in-progress parse. 342 | */ 343 | struct JsonParser final { 344 | 345 | /* State 346 | */ 347 | const string &str; 348 | size_t i; 349 | string &err; 350 | bool failed; 351 | const JsonParse strategy; 352 | 353 | /* fail(msg, err_ret = Json()) 354 | * 355 | * Mark this parse as failed. 356 | */ 357 | Json fail(string &&msg) { 358 | return fail(std::move(msg), Json()); 359 | } 360 | 361 | template 362 | T fail(string &&msg, const T err_ret) { 363 | if (!failed) 364 | err = std::move(msg); 365 | failed = true; 366 | return err_ret; 367 | } 368 | 369 | /* consume_whitespace() 370 | * 371 | * Advance until the current character is non-whitespace. 372 | */ 373 | void consume_whitespace() { 374 | while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t') 375 | i++; 376 | } 377 | 378 | /* consume_comment() 379 | * 380 | * Advance comments (c-style inline and multiline). 381 | */ 382 | bool consume_comment() { 383 | bool comment_found = false; 384 | if (str[i] == '/') { 385 | i++; 386 | if (i == str.size()) 387 | return fail("unexpected end of input inside comment", false); 388 | if (str[i] == '/') { // inline comment 389 | i++; 390 | if (i == str.size()) 391 | return fail("unexpected end of input inside inline comment", false); 392 | // advance until next line 393 | while (str[i] != '\n') { 394 | i++; 395 | if (i == str.size()) 396 | return fail("unexpected end of input inside inline comment", false); 397 | } 398 | comment_found = true; 399 | } 400 | else if (str[i] == '*') { // multiline comment 401 | i++; 402 | if (i > str.size()-2) 403 | return fail("unexpected end of input inside multi-line comment", false); 404 | // advance until closing tokens 405 | while (!(str[i] == '*' && str[i+1] == '/')) { 406 | i++; 407 | if (i > str.size()-2) 408 | return fail( 409 | "unexpected end of input inside multi-line comment", false); 410 | } 411 | i += 2; 412 | if (i == str.size()) 413 | return fail( 414 | "unexpected end of input inside multi-line comment", false); 415 | comment_found = true; 416 | } 417 | else 418 | return fail("malformed comment", false); 419 | } 420 | return comment_found; 421 | } 422 | 423 | /* consume_garbage() 424 | * 425 | * Advance until the current character is non-whitespace and non-comment. 426 | */ 427 | void consume_garbage() { 428 | consume_whitespace(); 429 | if(strategy == JsonParse::COMMENTS) { 430 | bool comment_found = false; 431 | do { 432 | comment_found = consume_comment(); 433 | consume_whitespace(); 434 | } 435 | while(comment_found); 436 | } 437 | } 438 | 439 | /* get_next_token() 440 | * 441 | * Return the next non-whitespace character. If the end of the input is reached, 442 | * flag an error and return 0. 443 | */ 444 | char get_next_token() { 445 | consume_garbage(); 446 | if (i == str.size()) 447 | return fail("unexpected end of input", (char)0); 448 | 449 | return str[i++]; 450 | } 451 | 452 | /* encode_utf8(pt, out) 453 | * 454 | * Encode pt as UTF-8 and add it to out. 455 | */ 456 | void encode_utf8(long pt, string & out) { 457 | if (pt < 0) 458 | return; 459 | 460 | if (pt < 0x80) { 461 | out += static_cast(pt); 462 | } else if (pt < 0x800) { 463 | out += static_cast((pt >> 6) | 0xC0); 464 | out += static_cast((pt & 0x3F) | 0x80); 465 | } else if (pt < 0x10000) { 466 | out += static_cast((pt >> 12) | 0xE0); 467 | out += static_cast(((pt >> 6) & 0x3F) | 0x80); 468 | out += static_cast((pt & 0x3F) | 0x80); 469 | } else { 470 | out += static_cast((pt >> 18) | 0xF0); 471 | out += static_cast(((pt >> 12) & 0x3F) | 0x80); 472 | out += static_cast(((pt >> 6) & 0x3F) | 0x80); 473 | out += static_cast((pt & 0x3F) | 0x80); 474 | } 475 | } 476 | 477 | /* parse_string() 478 | * 479 | * Parse a string, starting at the current position. 480 | */ 481 | string parse_string() { 482 | string out; 483 | long last_escaped_codepoint = -1; 484 | while (true) { 485 | if (i == str.size()) 486 | return fail("unexpected end of input in string", ""); 487 | 488 | char ch = str[i++]; 489 | 490 | if (ch == '"') { 491 | encode_utf8(last_escaped_codepoint, out); 492 | return out; 493 | } 494 | 495 | if (in_range(ch, 0, 0x1f)) 496 | return fail("unescaped " + esc(ch) + " in string", ""); 497 | 498 | // The usual case: non-escaped characters 499 | if (ch != '\\') { 500 | encode_utf8(last_escaped_codepoint, out); 501 | last_escaped_codepoint = -1; 502 | out += ch; 503 | continue; 504 | } 505 | 506 | // Handle escapes 507 | if (i == str.size()) 508 | return fail("unexpected end of input in string", ""); 509 | 510 | ch = str[i++]; 511 | 512 | if (ch == 'u') { 513 | // Extract 4-byte escape sequence 514 | string esc = str.substr(i, 4); 515 | // Explicitly check length of the substring. The following loop 516 | // relies on std::string returning the terminating NUL when 517 | // accessing str[length]. Checking here reduces brittleness. 518 | if (esc.length() < 4) { 519 | return fail("bad \\u escape: " + esc, ""); 520 | } 521 | for (size_t j = 0; j < 4; j++) { 522 | if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F') 523 | && !in_range(esc[j], '0', '9')) 524 | return fail("bad \\u escape: " + esc, ""); 525 | } 526 | 527 | long codepoint = strtol(esc.data(), nullptr, 16); 528 | 529 | // JSON specifies that characters outside the BMP shall be encoded as a pair 530 | // of 4-hex-digit \u escapes encoding their surrogate pair components. Check 531 | // whether we're in the middle of such a beast: the previous codepoint was an 532 | // escaped lead (high) surrogate, and this is a trail (low) surrogate. 533 | if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF) 534 | && in_range(codepoint, 0xDC00, 0xDFFF)) { 535 | // Reassemble the two surrogate pairs into one astral-plane character, per 536 | // the UTF-16 algorithm. 537 | encode_utf8((((last_escaped_codepoint - 0xD800) << 10) 538 | | (codepoint - 0xDC00)) + 0x10000, out); 539 | last_escaped_codepoint = -1; 540 | } else { 541 | encode_utf8(last_escaped_codepoint, out); 542 | last_escaped_codepoint = codepoint; 543 | } 544 | 545 | i += 4; 546 | continue; 547 | } 548 | 549 | encode_utf8(last_escaped_codepoint, out); 550 | last_escaped_codepoint = -1; 551 | 552 | if (ch == 'b') { 553 | out += '\b'; 554 | } else if (ch == 'f') { 555 | out += '\f'; 556 | } else if (ch == 'n') { 557 | out += '\n'; 558 | } else if (ch == 'r') { 559 | out += '\r'; 560 | } else if (ch == 't') { 561 | out += '\t'; 562 | } else if (ch == '"' || ch == '\\' || ch == '/') { 563 | out += ch; 564 | } else { 565 | return fail("invalid escape character " + esc(ch), ""); 566 | } 567 | } 568 | } 569 | 570 | /* parse_number() 571 | * 572 | * Parse a double. 573 | */ 574 | Json parse_number() { 575 | size_t start_pos = i; 576 | 577 | if (str[i] == '-') 578 | i++; 579 | 580 | // Integer part 581 | if (str[i] == '0') { 582 | i++; 583 | if (in_range(str[i], '0', '9')) 584 | return fail("leading 0s not permitted in numbers"); 585 | } else if (in_range(str[i], '1', '9')) { 586 | i++; 587 | while (in_range(str[i], '0', '9')) 588 | i++; 589 | } else { 590 | return fail("invalid " + esc(str[i]) + " in number"); 591 | } 592 | 593 | if (str[i] != '.' && str[i] != 'e' && str[i] != 'E' 594 | && (i - start_pos) <= static_cast(std::numeric_limits::digits10)) { 595 | return std::atoi(str.c_str() + start_pos); 596 | } 597 | 598 | // Decimal part 599 | if (str[i] == '.') { 600 | i++; 601 | if (!in_range(str[i], '0', '9')) 602 | return fail("at least one digit required in fractional part"); 603 | 604 | while (in_range(str[i], '0', '9')) 605 | i++; 606 | } 607 | 608 | // Exponent part 609 | if (str[i] == 'e' || str[i] == 'E') { 610 | i++; 611 | 612 | if (str[i] == '+' || str[i] == '-') 613 | i++; 614 | 615 | if (!in_range(str[i], '0', '9')) 616 | return fail("at least one digit required in exponent"); 617 | 618 | while (in_range(str[i], '0', '9')) 619 | i++; 620 | } 621 | 622 | return std::strtod(str.c_str() + start_pos, nullptr); 623 | } 624 | 625 | /* expect(str, res) 626 | * 627 | * Expect that 'str' starts at the character that was just read. If it does, advance 628 | * the input and return res. If not, flag an error. 629 | */ 630 | Json expect(const string &expected, Json res) { 631 | assert(i != 0); 632 | i--; 633 | if (str.compare(i, expected.length(), expected) == 0) { 634 | i += expected.length(); 635 | return res; 636 | } else { 637 | return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length())); 638 | } 639 | } 640 | 641 | /* parse_json() 642 | * 643 | * Parse a JSON object. 644 | */ 645 | Json parse_json(int depth) { 646 | if (depth > max_depth) { 647 | return fail("exceeded maximum nesting depth"); 648 | } 649 | 650 | char ch = get_next_token(); 651 | if (failed) 652 | return Json(); 653 | 654 | if (ch == '-' || (ch >= '0' && ch <= '9')) { 655 | i--; 656 | return parse_number(); 657 | } 658 | 659 | if (ch == 't') 660 | return expect("true", true); 661 | 662 | if (ch == 'f') 663 | return expect("false", false); 664 | 665 | if (ch == 'n') 666 | return expect("null", Json()); 667 | 668 | if (ch == '"') 669 | return parse_string(); 670 | 671 | if (ch == '{') { 672 | map data; 673 | ch = get_next_token(); 674 | if (ch == '}') 675 | return data; 676 | 677 | while (1) { 678 | if (ch != '"') 679 | return fail("expected '\"' in object, got " + esc(ch)); 680 | 681 | string key = parse_string(); 682 | if (failed) 683 | return Json(); 684 | 685 | ch = get_next_token(); 686 | if (ch != ':') 687 | return fail("expected ':' in object, got " + esc(ch)); 688 | 689 | data[std::move(key)] = parse_json(depth + 1); 690 | if (failed) 691 | return Json(); 692 | 693 | ch = get_next_token(); 694 | if (ch == '}') 695 | break; 696 | if (ch != ',') 697 | return fail("expected ',' in object, got " + esc(ch)); 698 | 699 | ch = get_next_token(); 700 | } 701 | return data; 702 | } 703 | 704 | if (ch == '[') { 705 | vector data; 706 | ch = get_next_token(); 707 | if (ch == ']') 708 | return data; 709 | 710 | while (1) { 711 | i--; 712 | data.push_back(parse_json(depth + 1)); 713 | if (failed) 714 | return Json(); 715 | 716 | ch = get_next_token(); 717 | if (ch == ']') 718 | break; 719 | if (ch != ',') 720 | return fail("expected ',' in list, got " + esc(ch)); 721 | 722 | ch = get_next_token(); 723 | (void)ch; 724 | } 725 | return data; 726 | } 727 | 728 | return fail("expected value, got " + esc(ch)); 729 | } 730 | }; 731 | }//namespace { 732 | 733 | Json Json::parse(const string &in, string &err, JsonParse strategy) { 734 | JsonParser parser { in, 0, err, false, strategy }; 735 | Json result = parser.parse_json(0); 736 | 737 | // Check for any trailing garbage 738 | parser.consume_garbage(); 739 | if (parser.i != in.size()) 740 | return parser.fail("unexpected trailing " + esc(in[parser.i])); 741 | 742 | return result; 743 | } 744 | 745 | // Documented in json11.hpp 746 | vector Json::parse_multi(const string &in, 747 | std::string::size_type &parser_stop_pos, 748 | string &err, 749 | JsonParse strategy) { 750 | JsonParser parser { in, 0, err, false, strategy }; 751 | parser_stop_pos = 0; 752 | vector json_vec; 753 | while (parser.i != in.size() && !parser.failed) { 754 | json_vec.push_back(parser.parse_json(0)); 755 | // Check for another object 756 | parser.consume_garbage(); 757 | if (!parser.failed) 758 | parser_stop_pos = parser.i; 759 | } 760 | return json_vec; 761 | } 762 | 763 | /* * * * * * * * * * * * * * * * * * * * 764 | * Shape-checking 765 | */ 766 | 767 | bool Json::has_shape(const shape & types, string & err) const { 768 | if (!is_object()) { 769 | err = "expected JSON object, got " + dump(); 770 | return false; 771 | } 772 | 773 | for (auto & item : types) { 774 | if ((*this)[item.first].type() != item.second) { 775 | err = "bad type for " + item.first + " in " + dump(); 776 | return false; 777 | } 778 | } 779 | 780 | return true; 781 | } 782 | 783 | } // namespace json11 784 | -------------------------------------------------------------------------------- /src/json11.h: -------------------------------------------------------------------------------- 1 | /* json11 2 | * 3 | * json11 is a tiny JSON library for C++11, providing JSON parsing and serialization. 4 | * 5 | * The core object provided by the library is json11::Json. A Json object represents any JSON 6 | * value: null, bool, number (int or double), string (std::string), array (std::vector), or 7 | * object (std::map). 8 | * 9 | * Json objects act like values: they can be assigned, copied, moved, compared for equality or 10 | * order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and 11 | * Json::parse (static) to parse a std::string as a Json object. 12 | * 13 | * Internally, the various types of Json object are represented by the JsonValue class 14 | * hierarchy. 15 | * 16 | * A note on numbers - JSON specifies the syntax of number formatting but not its semantics, 17 | * so some JSON implementations distinguish between integers and floating-point numbers, while 18 | * some don't. In json11, we choose the latter. Because some JSON implementations (namely 19 | * Javascript itself) treat all numbers as the same type, distinguishing the two leads 20 | * to JSON that will be *silently* changed by a round-trip through those implementations. 21 | * Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also 22 | * provides integer helpers. 23 | * 24 | * Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the 25 | * range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64 26 | * or long long to avoid the Y2038K problem; a double storing microseconds since some epoch 27 | * will be exact for +/- 275 years.) 28 | */ 29 | 30 | /* Copyright (c) 2013 Dropbox, Inc. 31 | * 32 | * Permission is hereby granted, free of charge, to any person obtaining a copy 33 | * of this software and associated documentation files (the "Software"), to deal 34 | * in the Software without restriction, including without limitation the rights 35 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 36 | * copies of the Software, and to permit persons to whom the Software is 37 | * furnished to do so, subject to the following conditions: 38 | * 39 | * The above copyright notice and this permission notice shall be included in 40 | * all copies or substantial portions of the Software. 41 | * 42 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 43 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 44 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 45 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 46 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 47 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 48 | * THE SOFTWARE. 49 | */ 50 | 51 | #pragma once 52 | 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | #ifdef _MSC_VER 60 | #if _MSC_VER <= 1800 // VS 2013 61 | #ifndef noexcept 62 | #define noexcept throw() 63 | #endif 64 | 65 | #ifndef snprintf 66 | #define snprintf _snprintf_s 67 | #endif 68 | #endif 69 | #endif 70 | 71 | namespace json11 { 72 | 73 | enum JsonParse { 74 | STANDARD, COMMENTS 75 | }; 76 | 77 | class JsonValue; 78 | 79 | class Json final { 80 | public: 81 | // Types 82 | enum Type { 83 | NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT 84 | }; 85 | 86 | // Array and object typedefs 87 | typedef std::vector array; 88 | typedef std::map object; 89 | 90 | // Constructors for the various types of JSON value. 91 | Json() noexcept; // NUL 92 | Json(std::nullptr_t) noexcept; // NUL 93 | Json(double value); // NUMBER 94 | Json(int value); // NUMBER 95 | Json(bool value); // BOOL 96 | Json(const std::string &value); // STRING 97 | Json(std::string &&value); // STRING 98 | Json(const char * value); // STRING 99 | Json(const array &values); // ARRAY 100 | Json(array &&values); // ARRAY 101 | Json(const object &values); // OBJECT 102 | Json(object &&values); // OBJECT 103 | 104 | // Implicit constructor: anything with a to_json() function. 105 | template 106 | Json(const T & t) : Json(t.to_json()) {} 107 | 108 | // Implicit constructor: map-like objects (std::map, std::unordered_map, etc) 109 | template ::value 111 | && std::is_constructible::value, 112 | int>::type = 0> 113 | Json(const M & m) : Json(object(m.begin(), m.end())) {} 114 | 115 | // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc) 116 | template ::value, 118 | int>::type = 0> 119 | Json(const V & v) : Json(array(v.begin(), v.end())) {} 120 | 121 | // This prevents Json(some_pointer) from accidentally producing a bool. Use 122 | // Json(bool(some_pointer)) if that behavior is desired. 123 | Json(void *) = delete; 124 | 125 | // Accessors 126 | Type type() const; 127 | 128 | bool is_null() const { return type() == NUL; } 129 | bool is_number() const { return type() == NUMBER; } 130 | bool is_bool() const { return type() == BOOL; } 131 | bool is_string() const { return type() == STRING; } 132 | bool is_array() const { return type() == ARRAY; } 133 | bool is_object() const { return type() == OBJECT; } 134 | 135 | // Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not 136 | // distinguish between integer and non-integer numbers - number_value() and int_value() 137 | // can both be applied to a NUMBER-typed object. 138 | double number_value() const; 139 | int int_value() const; 140 | 141 | // Return the enclosed value if this is a boolean, false otherwise. 142 | bool bool_value() const; 143 | // Return the enclosed string if this is a string, "" otherwise. 144 | const std::string &string_value() const; 145 | // Return the enclosed std::vector if this is an array, or an empty vector otherwise. 146 | const array &array_items() const; 147 | // Return the enclosed std::map if this is an object, or an empty map otherwise. 148 | const object &object_items() const; 149 | 150 | // Return a reference to arr[i] if this is an array, Json() otherwise. 151 | const Json & operator[](size_t i) const; 152 | // Return a reference to obj[key] if this is an object, Json() otherwise. 153 | const Json & operator[](const std::string &key) const; 154 | 155 | // Serialize. 156 | void dump(std::string &out) const; 157 | std::string dump() const { 158 | std::string out; 159 | dump(out); 160 | return out; 161 | } 162 | 163 | // Parse. If parse fails, return Json() and assign an error message to err. 164 | static Json parse(const std::string & in, 165 | std::string & err, 166 | JsonParse strategy = JsonParse::STANDARD); 167 | static Json parse(const char * in, 168 | std::string & err, 169 | JsonParse strategy = JsonParse::STANDARD) { 170 | if (in) { 171 | return parse(std::string(in), err, strategy); 172 | } else { 173 | err = "null input"; 174 | return nullptr; 175 | } 176 | } 177 | // Parse multiple objects, concatenated or separated by whitespace 178 | static std::vector parse_multi( 179 | const std::string & in, 180 | std::string::size_type & parser_stop_pos, 181 | std::string & err, 182 | JsonParse strategy = JsonParse::STANDARD); 183 | 184 | static inline std::vector parse_multi( 185 | const std::string & in, 186 | std::string & err, 187 | JsonParse strategy = JsonParse::STANDARD) { 188 | std::string::size_type parser_stop_pos; 189 | return parse_multi(in, parser_stop_pos, err, strategy); 190 | } 191 | 192 | bool operator== (const Json &rhs) const; 193 | bool operator< (const Json &rhs) const; 194 | bool operator!= (const Json &rhs) const { return !(*this == rhs); } 195 | bool operator<= (const Json &rhs) const { return !(rhs < *this); } 196 | bool operator> (const Json &rhs) const { return (rhs < *this); } 197 | bool operator>= (const Json &rhs) const { return !(*this < rhs); } 198 | 199 | /* has_shape(types, err) 200 | * 201 | * Return true if this is a JSON object and, for each item in types, has a field of 202 | * the given type. If not, return false and set err to a descriptive message. 203 | */ 204 | typedef std::initializer_list> shape; 205 | bool has_shape(const shape & types, std::string & err) const; 206 | 207 | private: 208 | std::shared_ptr m_ptr; 209 | }; 210 | 211 | // Internal class hierarchy - JsonValue objects are not exposed to users of this API. 212 | class JsonValue { 213 | protected: 214 | friend class Json; 215 | friend class JsonInt; 216 | friend class JsonDouble; 217 | virtual Json::Type type() const = 0; 218 | virtual bool equals(const JsonValue * other) const = 0; 219 | virtual bool less(const JsonValue * other) const = 0; 220 | virtual void dump(std::string &out) const = 0; 221 | virtual double number_value() const; 222 | virtual int int_value() const; 223 | virtual bool bool_value() const; 224 | virtual const std::string &string_value() const; 225 | virtual const Json::array &array_items() const; 226 | virtual const Json &operator[](size_t i) const; 227 | virtual const Json::object &object_items() const; 228 | virtual const Json &operator[](const std::string &key) const; 229 | virtual ~JsonValue() {} 230 | }; 231 | 232 | } // namespace json11 233 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(geojsonR) 3 | 4 | test_check("geojsonR") 5 | -------------------------------------------------------------------------------- /tests/testthat/.Rhistory: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlampros/geojsonR/99bfb444b7f6b53250c33321d4b4d3c1994c3142/tests/testthat/.Rhistory -------------------------------------------------------------------------------- /tests/testthat/file_data/Feature.geojson: -------------------------------------------------------------------------------- 1 | 2 | { "type": "Feature", 3 | 4 | "id": 1, 5 | 6 | "bbox": [-10.02, -10.02, 10.02, 10.02], 7 | 8 | "geometry": { 9 | 10 | "type": "Point", 11 | 12 | "coordinates": [61.210817, 35.650072] 13 | }, 14 | 15 | "properties": { 16 | 17 | "location": "some location", 18 | 19 | "details": "include details about location" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/testthat/file_data/feature_multiple_files/Feature1.geojson: -------------------------------------------------------------------------------- 1 | 2 | { "type": "Feature", 3 | 4 | "id": 1, 5 | 6 | "bbox": [-10.02, -10.02, 10.02, 10.02], 7 | 8 | "geometry": { 9 | 10 | "type": "Point", 11 | 12 | "coordinates": [61.210817, 35.650072] 13 | }, 14 | 15 | "properties": { 16 | 17 | "location": "some location", 18 | 19 | "details": "include details about location" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/testthat/file_data/feature_multiple_files/Feature2.geojson: -------------------------------------------------------------------------------- 1 | 2 | { "type": "Feature", 3 | 4 | "id": 1, 5 | 6 | "bbox": [-10.02, -10.02, 10.02, 10.02], 7 | 8 | "geometry": { 9 | 10 | "type": "Point", 11 | 12 | "coordinates": [61.210817, 35.650072] 13 | }, 14 | 15 | "properties": { 16 | 17 | "location": "some location", 18 | 19 | "details": "include details about location" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/testthat/file_data/feature_multiple_files/Feature3.geojson: -------------------------------------------------------------------------------- 1 | 2 | { "type": "Feature", 3 | 4 | "id": 1, 5 | 6 | "bbox": [-10.02, -10.02, 10.02, 10.02], 7 | 8 | "geometry": { 9 | 10 | "type": "Point", 11 | 12 | "coordinates": [61.210817, 35.650072] 13 | }, 14 | 15 | "properties": { 16 | 17 | "location": "some location", 18 | 19 | "details": "include details about location" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/testthat/file_data/feature_multiple_files/Feature4.geojson: -------------------------------------------------------------------------------- 1 | 2 | { "type": "Feature", 3 | 4 | "id": 1, 5 | 6 | "bbox": [-10.02, -10.02, 10.02, 10.02], 7 | 8 | "geometry": { 9 | 10 | "type": "Point", 11 | 12 | "coordinates": [61.210817, 35.650072] 13 | }, 14 | 15 | "properties": { 16 | 17 | "location": "some location", 18 | 19 | "details": "include details about location" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/testthat/file_data/feature_multiple_files/Feature5.geojson: -------------------------------------------------------------------------------- 1 | 2 | { "type": "Feature", 3 | 4 | "id": 1, 5 | 6 | "bbox": [-10.02, -10.02, 10.02, 10.02], 7 | 8 | "geometry": { 9 | 10 | "type": "Point", 11 | 12 | "coordinates": [61.210817, 35.650072] 13 | }, 14 | 15 | "properties": { 16 | 17 | "location": "some location", 18 | 19 | "details": "include details about location" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/testthat/merge_folder/example1.json: -------------------------------------------------------------------------------- 1 | {"id":"2489368199","type":"IssueCommentEvent"} 2 | -------------------------------------------------------------------------------- /tests/testthat/merge_folder/example2.json: -------------------------------------------------------------------------------- 1 | {"id":"2489368070","type":"PushEvent"} 2 | -------------------------------------------------------------------------------- /tests/testthat/test-geojson.R: -------------------------------------------------------------------------------- 1 | 2 | #=========================================================================== 3 | 4 | # data geometry object for all functions except for the FROM_GeoJson_Schema 5 | 6 | js_data = '{ "type": "MultiPolygon", "coordinates": [ 7 | [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]], 8 | [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]], 9 | [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]] 10 | ] 11 | }' 12 | 13 | 14 | # data for the 'FROM_GeoJson_Schema' function 15 | 16 | schema_str = '{ 17 | "name" : "example_name", 18 | "location" : { 19 | "type" : "Point", 20 | "coordinates" : [ -120.24, 39.21 ] 21 | } 22 | }' 23 | 24 | 25 | #=========================================================================== 26 | 27 | 28 | # data (geometry collection) ----------------------- 29 | 30 | 31 | Point = c(100, 1.01) 32 | 33 | MultiPoint = list(c(100, 1.01), c(200, 2.01)) 34 | 35 | MultiLineString = list(list(c(100, 1.01), c(200, 2.01)), 36 | 37 | list(c(100, 1.01), c(200, 2.01))) 38 | 39 | LineString = list(c(100, 1.01), c(200, 2.01)) 40 | 41 | MultiLineString = list(list(c(100, 1.01), c(200, 2.01)), 42 | 43 | list(c(100, 1.01), c(200, 2.01))) 44 | 45 | Polygon = list(list(c(100, 1.01), c(200, 2.01), c(100, 1.01), c(200, 2.01))) 46 | 47 | Polygon = list(list(c(100, 1.01), c(200, 2.01), c(100, 1.01), c(200, 2.01)), 48 | 49 | list(c(100, 1.01), c(200, 2.01), c(100, 1.01), c(200, 2.01))) 50 | 51 | MultiPolygon = list(list(list(c(100, 1.0), c(200, 2.0), c(100, 1.0), c(200, 2.0))), 52 | 53 | list(list(c(100, 1.0), c(200, 2.0), c(100, 1.0), c(200, 2.0)), 54 | 55 | list(c(100, 1.0), c(200, 2.0), c(100, 1.0), c(200, 2.0)))) 56 | 57 | 58 | geometry_collection_dat = list(Point = Point, MultiPoint = MultiPoint, 59 | 60 | MultiLineString = MultiLineString, LineString = LineString, 61 | 62 | MultiLineString = MultiLineString, Polygon = Polygon, 63 | 64 | Polygon = Polygon, MultiPolygon = MultiPolygon) 65 | 66 | 67 | 68 | # data (Feature) ------------------------------------------- 69 | 70 | 71 | # Empty 'properties' list 72 | 73 | feature_dat1 = list(id = 1, bbox = c(1,2,3,4), geometry = list(Point = c(100, 1.01)), 74 | 75 | properties = list()) 76 | 77 | 78 | # Nested 'properties' list 79 | 80 | feature_dat2 = list(id = "1", bbox = c(1,2,3,4), geometry = list(Point = c(100, 1.01)), 81 | 82 | properties = list(prop0 = 'value0', 83 | 84 | prop1 = 0.0, vec = c(1,2,3), lst = list(a = 1, d = 2))) 85 | 86 | 87 | # data (FeatureCollection) ----------------------------------- 88 | 89 | 90 | feature_col_dat = list(bbox = c(-10.01, -10.01, 10.01, 10.01), 91 | 92 | features = list(Feature = feature_dat1, Feature = feature_dat2)) 93 | 94 | 95 | #=========================== 96 | context('geojson functions') 97 | #=========================== 98 | 99 | 100 | #---------------------- 101 | # FROM_GeoJson function 102 | #---------------------- 103 | 104 | 105 | 106 | testthat::test_that("in case that the 'url_file_string' parameter is not a character string it returns an error", { 107 | 108 | mt = matrix(runif(10), 2, 5) 109 | 110 | testthat::expect_error( FROM_GeoJson(url_file_string = mt) ) 111 | }) 112 | 113 | 114 | testthat::test_that("in case that the 'Flatten_Coords' parameter is not a boolean it returns an error", { 115 | 116 | mt = matrix(runif(10), 2, 5) 117 | 118 | testthat::expect_error( FROM_GeoJson(url_file_string = js_data, Flatten_Coords = mt) ) 119 | }) 120 | 121 | 122 | testthat::test_that("in case that the 'Average_Coordinates' parameter is not a boolean it returns an error", { 123 | 124 | mt = matrix(runif(10), 2, 5) 125 | 126 | testthat::expect_error( FROM_GeoJson(url_file_string = js_data, Average_Coordinates = mt) ) 127 | }) 128 | 129 | 130 | 131 | testthat::test_that("in case that the 'To_List' parameter is not a boolean it returns an error", { 132 | 133 | mt = matrix(runif(10), 2, 5) 134 | 135 | testthat::expect_error( FROM_GeoJson(url_file_string = js_data, To_List = mt) ) 136 | }) 137 | 138 | 139 | testthat::test_that("in case that the 'url_file_string' parameter is a geojson character string it returns a named list", { 140 | 141 | tmp = FROM_GeoJson(url_file_string = js_data) 142 | 143 | nams = sum(names(tmp) %in% c("type", "coordinates")) == 2 144 | 145 | obj = tmp$type == "MultiPolygon" 146 | 147 | len = length(tmp$coordinates) == 2 148 | 149 | testthat::expect_true(sum(c(nams, obj, len)) == 3) 150 | }) 151 | 152 | 153 | 154 | testthat::test_that("in case that the 'url_file_string' parameter is a character string path to a file it returns a named list", { 155 | 156 | PATH = paste0(getwd(), path.expand("/file_data/Feature.geojson")) 157 | 158 | tmp = FROM_GeoJson(url_file_string = PATH) 159 | 160 | nams = sum(names(tmp) %in% c("bbox", "geometry", "id", "properties", "type")) == 5 161 | 162 | obj = tmp$type == "Feature" 163 | 164 | len = length(tmp$geometry$coordinates) == 2 165 | 166 | testthat::expect_true(sum(c(nams, obj, len)) == 3) 167 | }) 168 | 169 | 170 | #----------------------------- 171 | # FROM_GeoJson_Schema function 172 | #----------------------------- 173 | 174 | 175 | 176 | testthat::test_that("in case that the 'url_file_string' parameter is not a character string it returns an error", { 177 | 178 | mt = matrix(runif(10), 2, 5) 179 | 180 | testthat::expect_error( FROM_GeoJson_Schema(url_file_string = mt) ) 181 | }) 182 | 183 | 184 | testthat::test_that("in case that the 'geometry_name' parameter is not a character string it returns an error", { 185 | 186 | mt = matrix(runif(10), 2, 5) 187 | 188 | testthat::expect_error( FROM_GeoJson_Schema(url_file_string = schema_str, geometry_name = mt) ) 189 | }) 190 | 191 | 192 | testthat::test_that("in case that the 'Average_Coordinates' parameter is not a boolean it returns an error", { 193 | 194 | mt = matrix(runif(10), 2, 5) 195 | 196 | testthat::expect_error( FROM_GeoJson_Schema(url_file_string = schema_str, Average_Coordinates = mt) ) 197 | }) 198 | 199 | 200 | 201 | testthat::test_that("in case that the 'To_List' parameter is not a boolean it returns an error", { 202 | 203 | mt = matrix(runif(10), 2, 5) 204 | 205 | testthat::expect_error( FROM_GeoJson_Schema(url_file_string = schema_str, To_List = mt) ) 206 | }) 207 | 208 | 209 | testthat::test_that("in case that the 'url_file_string' parameter is a geojson character string it returns a named list", { 210 | 211 | tmp = FROM_GeoJson_Schema(url_file_string = schema_str, geometry_name = "location") 212 | 213 | nams = sum(names(tmp) %in% c("location", "name")) == 2 214 | 215 | obj = tmp$location$type == "Point" 216 | 217 | len = length(tmp$location$coordinates) == 2 218 | 219 | testthat::expect_true(sum(c(nams, obj, len)) == 3) 220 | }) 221 | 222 | 223 | testthat::test_that("in case that the 'url_file_string' parameter is a character string path to a file it returns a named list", { 224 | 225 | PATH = paste0(getwd(), path.expand("/file_data/Schema_data.geojson")) 226 | 227 | tmp = FROM_GeoJson_Schema(url_file_string = PATH, geometry_name = "geometry") 228 | 229 | nams = sum(names(tmp) %in% c("_id", "geometry", "name")) == 3 230 | 231 | obj = tmp$geometry$type == "Polygon" 232 | 233 | len = sum(dim(tmp$geometry$coordinates) == c(4649, 2)) == 2 234 | 235 | testthat::expect_true(sum(c(nams, obj, len)) == 3) 236 | }) 237 | 238 | 239 | #--------------------------- 240 | # Dump_From_GeoJson function 241 | #--------------------------- 242 | 243 | 244 | testthat::test_that("in case that the 'url_file' parameter is not a character string it returns an error", { 245 | 246 | mt = matrix(runif(10), 2, 5) 247 | 248 | testthat::expect_error( Dump_From_GeoJson(url_file = mt) ) 249 | }) 250 | 251 | 252 | testthat::test_that("in case that the 'url_file' parameter is a character string BUT not a path to a file or a valid url it returns an error", { 253 | 254 | PATH = 'INVALID' 255 | 256 | testthat::expect_error( Dump_From_GeoJson(url_file = PATH) ) 257 | }) 258 | 259 | 260 | testthat::test_that("in case that the 'url_file' parameter is a valid path to a file it returns a ", { 261 | 262 | PATH = paste0(getwd(), path.expand("/file_data/Feature.geojson")) 263 | 264 | tmp = Dump_From_GeoJson(url_file = PATH) 265 | 266 | tmp_OUT = FROM_GeoJson(url_file_string = tmp) 267 | 268 | nams = sum(names(tmp_OUT) %in% c("bbox", "geometry", "id", "properties", "type")) == 5 269 | 270 | obj = tmp_OUT$type == "Feature" 271 | 272 | len = length(tmp_OUT$geometry$coordinates) == 2 273 | 274 | testthat::expect_true(sum(c(nams, obj, len)) == 3) 275 | }) 276 | 277 | 278 | 279 | #-------------------- 280 | # TO_GeoJson R6 class 281 | #-------------------- 282 | 283 | 284 | #==================================== 'Point' 285 | 286 | testthat::test_that("in case that the 'data' parameter is not a numeric vector it returns an error", { 287 | 288 | mt = matrix(runif(10), 2, 5) 289 | 290 | init = TO_GeoJson$new() 291 | 292 | testthat::expect_error( init$Point(mt, stringify = TRUE) ) 293 | }) 294 | 295 | 296 | 297 | testthat::test_that("in case that the 'stringify' parameter is not a boolean it returns an error", { 298 | 299 | init = TO_GeoJson$new() 300 | 301 | testthat::expect_error( init$Point(c(100, 200), stringify = 'TRUE') ) 302 | }) 303 | 304 | 305 | 306 | testthat::test_that("in case that both parameters are valid it returns a GeoJson object", { 307 | 308 | init = TO_GeoJson$new() 309 | 310 | res = init$Point(c(100, 200), stringify = TRUE) 311 | 312 | nams = sum(names(res) %in% c("json_dump", "type", "coordinates")) == 3 313 | 314 | TYPE = res$type == "Point" 315 | 316 | coords = is.vector(res$coordinates) && !length(res$coordinates) == 0 317 | 318 | testthat::expect_true( sum(c(nams, TYPE, coords)) == 3 ) 319 | }) 320 | 321 | 322 | 323 | #==================================== 'MultiPoint' 324 | 325 | testthat::test_that("in case that the 'data' parameter is not a numeric list it returns an error", { 326 | 327 | mt = matrix(runif(10), 2, 5) 328 | 329 | init = TO_GeoJson$new() 330 | 331 | testthat::expect_error( init$MultiPoint(mt, stringify = TRUE) ) 332 | }) 333 | 334 | 335 | 336 | testthat::test_that("in case that the 'stringify' parameter is not a boolean it returns an error", { 337 | 338 | init = TO_GeoJson$new() 339 | 340 | testthat::expect_error( init$MultiPoint(list(c(100, 1.01), c(200, 2.01)), stringify = 'TRUE') ) 341 | }) 342 | 343 | 344 | 345 | testthat::test_that("in case that both parameters are valid it returns a GeoJson object", { 346 | 347 | init = TO_GeoJson$new() 348 | 349 | res = init$MultiPoint(list(c(100, 1.01), c(200, 2.01)), stringify = TRUE) 350 | 351 | nams = sum(names(res) %in% c("json_dump", "type", "coordinates")) == 3 352 | 353 | TYPE = res$type == "MultiPoint" 354 | 355 | coords = is.list(res$coordinates) && !length(res$coordinates) == 0 356 | 357 | testthat::expect_true( sum(c(nams, TYPE, coords)) == 3 ) 358 | }) 359 | 360 | 361 | 362 | #==================================== 'LineString' 363 | 364 | testthat::test_that("in case that the 'data' parameter is not a numeric list it returns an error", { 365 | 366 | mt = matrix(runif(10), 2, 5) 367 | 368 | init = TO_GeoJson$new() 369 | 370 | testthat::expect_error( init$LineString(mt, stringify = TRUE) ) 371 | }) 372 | 373 | 374 | 375 | testthat::test_that("in case that the 'stringify' parameter is not a boolean it returns an error", { 376 | 377 | init = TO_GeoJson$new() 378 | 379 | testthat::expect_error( init$LineString(list(c(100, 1.01), c(200, 2.01)), stringify = 'TRUE') ) 380 | }) 381 | 382 | 383 | 384 | testthat::test_that("in case that both parameters are valid it returns a GeoJson object", { 385 | 386 | init = TO_GeoJson$new() 387 | 388 | res = init$LineString(list(c(100, 1.01), c(200, 2.01)), stringify = TRUE) 389 | 390 | nams = sum(names(res) %in% c("json_dump", "type", "coordinates")) == 3 391 | 392 | TYPE = res$type == "LineString" 393 | 394 | coords = is.list(res$coordinates) && !length(res$coordinates) == 0 395 | 396 | testthat::expect_true( sum(c(nams, TYPE, coords)) == 3 ) 397 | }) 398 | 399 | 400 | 401 | 402 | #==================================== 'MultiLineString' 403 | 404 | testthat::test_that("in case that the 'data' parameter is not a numeric list it returns an error", { 405 | 406 | mt = matrix(runif(10), 2, 5) 407 | 408 | init = TO_GeoJson$new() 409 | 410 | testthat::expect_error( init$MultiLineString(mt, stringify = TRUE) ) 411 | }) 412 | 413 | 414 | 415 | testthat::test_that("in case that the 'stringify' parameter is not a boolean it returns an error", { 416 | 417 | init = TO_GeoJson$new() 418 | 419 | testthat::expect_error( init$MultiLineString(list(list(c(100, 1.01), c(200, 2.01)), list(c(100, 1.01), c(200, 2.01))), stringify = 'TRUE') ) 420 | }) 421 | 422 | 423 | 424 | testthat::test_that("in case that both parameters are valid it returns a GeoJson object", { 425 | 426 | init = TO_GeoJson$new() 427 | 428 | res = init$MultiLineString(list(list(c(100, 1.01), c(200, 2.01)), list(c(100, 1.01), c(200, 2.01))), stringify = TRUE) 429 | 430 | nams = sum(names(res) %in% c("json_dump", "type", "coordinates")) == 3 431 | 432 | TYPE = res$type == "MultiLineString" 433 | 434 | coords = is.list(res$coordinates) && !length(res$coordinates) == 0 435 | 436 | testthat::expect_true( sum(c(nams, TYPE, coords)) == 3 ) 437 | }) 438 | 439 | 440 | #==================================== 'Polygon (WITHOUT interior rings)' 441 | 442 | testthat::test_that("in case that the 'data' parameter is not a numeric list it returns an error", { 443 | 444 | mt = matrix(runif(10), 2, 5) 445 | 446 | init = TO_GeoJson$new() 447 | 448 | testthat::expect_error( init$Polygon(mt, stringify = TRUE) ) 449 | }) 450 | 451 | 452 | 453 | testthat::test_that("in case that the 'stringify' parameter is not a boolean it returns an error", { 454 | 455 | init = TO_GeoJson$new() 456 | 457 | testthat::expect_error( init$Polygon(list(list(c(100, 1.01), c(200, 2.01), c(100, 1.01), c(200, 2.01))), stringify = 'TRUE') ) 458 | }) 459 | 460 | 461 | 462 | testthat::test_that("in case that both parameters are valid it returns a GeoJson object", { 463 | 464 | init = TO_GeoJson$new() 465 | 466 | res = init$Polygon(list(list(c(100, 1.01), c(200, 2.01), c(100, 1.01), c(200, 2.01))), stringify = TRUE) 467 | 468 | nams = sum(names(res) %in% c("json_dump", "type", "coordinates")) == 3 469 | 470 | TYPE = res$type == "Polygon" 471 | 472 | coords = is.list(res$coordinates) && !length(res$coordinates) == 0 473 | 474 | testthat::expect_true( sum(c(nams, TYPE, coords)) == 3 ) 475 | }) 476 | 477 | 478 | 479 | #==================================== 'Polygon (WITH interior rings)' 480 | 481 | testthat::test_that("in case that the 'data' parameter is not a numeric list it returns an error", { 482 | 483 | mt = matrix(runif(10), 2, 5) 484 | 485 | init = TO_GeoJson$new() 486 | 487 | testthat::expect_error( init$Polygon(mt, stringify = TRUE) ) 488 | }) 489 | 490 | 491 | 492 | testthat::test_that("in case that the 'stringify' parameter is not a boolean it returns an error", { 493 | 494 | init = TO_GeoJson$new() 495 | 496 | testthat::expect_error( init$Polygon( list(list(c(100, 1.01), c(200, 2.01), c(100, 1.01), c(200, 2.01)), 497 | 498 | list(c(100, 1.01), c(200, 2.01), c(100, 1.01), c(200, 2.01))), stringify = 'TRUE') ) 499 | }) 500 | 501 | 502 | 503 | testthat::test_that("in case that both parameters are valid it returns a GeoJson object", { 504 | 505 | init = TO_GeoJson$new() 506 | 507 | res = init$Polygon( list(list(c(100, 1.01), c(200, 2.01), c(100, 1.01), c(200, 2.01)), 508 | 509 | list(c(100, 1.01), c(200, 2.01), c(100, 1.01), c(200, 2.01))), stringify = TRUE) 510 | 511 | nams = sum(names(res) %in% c("json_dump", "type", "coordinates")) == 3 512 | 513 | TYPE = res$type == "Polygon" 514 | 515 | coords = is.list(res$coordinates) && !length(res$coordinates) == 0 516 | 517 | testthat::expect_true( sum(c(nams, TYPE, coords)) == 3 ) 518 | }) 519 | 520 | 521 | 522 | #==================================== 'MultiPolygon' 523 | 524 | testthat::test_that("in case that the 'data' parameter is not a numeric list it returns an error", { 525 | 526 | mt = matrix(runif(10), 2, 5) 527 | 528 | init = TO_GeoJson$new() 529 | 530 | testthat::expect_error( init$MultiPolygon(mt, stringify = TRUE) ) 531 | }) 532 | 533 | 534 | 535 | testthat::test_that("in case that the 'stringify' parameter is not a boolean it returns an error", { 536 | 537 | init = TO_GeoJson$new() 538 | 539 | testthat::expect_error( init$MultiPolygon(list(list(list(c(100, 1.0), c(200, 2.0), c(100, 1.0), c(200, 2.0))), 540 | 541 | list(list(c(100, 1.0), c(200, 2.0), c(100, 1.0), c(200, 2.0)), 542 | 543 | list(c(100, 1.0), c(200, 2.0), c(100, 1.0), c(200, 2.0)))), stringify = 'TRUE') ) 544 | }) 545 | 546 | 547 | 548 | testthat::test_that("in case that both parameters are valid it returns a GeoJson object", { 549 | 550 | init = TO_GeoJson$new() 551 | 552 | res = init$MultiPolygon(list(list(list(c(100, 1.0), c(200, 2.0), c(100, 1.0), c(200, 2.0))), 553 | 554 | list(list(c(100, 1.0), c(200, 2.0), c(100, 1.0), c(200, 2.0)), 555 | 556 | list(c(100, 1.0), c(200, 2.0), c(100, 1.0), c(200, 2.0)))), stringify = TRUE) 557 | 558 | nams = sum(names(res) %in% c("json_dump", "type", "coordinates")) == 3 559 | 560 | TYPE = res$type == "MultiPolygon" 561 | 562 | coords = is.list(res$coordinates) && !length(res$coordinates) == 0 563 | 564 | testthat::expect_true( sum(c(nams, TYPE, coords)) == 3 ) 565 | }) 566 | 567 | 568 | 569 | #==================================== 'GeometryCollection' 570 | 571 | testthat::test_that("in case that the 'data' parameter is not a numeric list it returns an error", { 572 | 573 | mt = matrix(runif(10), 2, 5) 574 | 575 | init = TO_GeoJson$new() 576 | 577 | testthat::expect_error( init$GeometryCollection(mt, stringify = TRUE) ) 578 | }) 579 | 580 | 581 | 582 | testthat::test_that("in case that the 'stringify' parameter is not a boolean it returns an error", { 583 | 584 | init = TO_GeoJson$new() 585 | 586 | testthat::expect_error( init$GeometryCollection(geometry_collection_dat, stringify = 'TRUE') ) 587 | }) 588 | 589 | 590 | 591 | testthat::test_that("in case that both parameters are valid it returns a GeoJson object", { 592 | 593 | init = TO_GeoJson$new() 594 | 595 | res = init$GeometryCollection(geometry_collection_dat, stringify = TRUE) 596 | 597 | nams = sum(names(res) %in% c("json_dump", "type", "geometries")) == 3 598 | 599 | TYPE = res$type == "GeometryCollection" 600 | 601 | coords = is.list(res$geometries) && !length(res$geometries) == 0 602 | 603 | testthat::expect_true( sum(c(nams, TYPE, coords)) == 3 ) 604 | }) 605 | 606 | 607 | 608 | #==================================== 'Feature' 609 | 610 | testthat::test_that("in case that the 'data' parameter is not a list it returns an error", { 611 | 612 | mt = matrix(runif(10), 2, 5) 613 | 614 | init = TO_GeoJson$new() 615 | 616 | testthat::expect_error( init$Feature(mt, stringify = TRUE) ) 617 | }) 618 | 619 | 620 | 621 | testthat::test_that("in case that the 'stringify' parameter is not a boolean it returns an error", { 622 | 623 | init = TO_GeoJson$new() 624 | 625 | testthat::expect_error( init$Feature(feature_dat2, stringify = 'TRUE') ) 626 | }) 627 | 628 | 629 | 630 | testthat::test_that("in case that both parameters are valid it returns a GeoJson object", { 631 | 632 | init = TO_GeoJson$new() 633 | 634 | res = init$Feature(feature_dat2, stringify = TRUE) 635 | 636 | nams = sum(names(res) %in% c("id", "bbox", "geometry", "properties", "json_dump", "type")) == 6 637 | 638 | TYPE = res$type == "Feature" 639 | 640 | coords = is.list(res$geometry) && !length(res$geometry) == 0 641 | 642 | testthat::expect_true( sum(c(nams, TYPE, coords)) == 3 ) 643 | }) 644 | 645 | 646 | #==================================== 'FeatureCollection' 647 | 648 | testthat::test_that("in case that the 'data' parameter is not a list it returns an error", { 649 | 650 | mt = matrix(runif(10), 2, 5) 651 | 652 | init = TO_GeoJson$new() 653 | 654 | testthat::expect_error( init$FeatureCollection(mt, stringify = TRUE) ) 655 | }) 656 | 657 | 658 | 659 | testthat::test_that("in case that the 'stringify' parameter is not a boolean it returns an error", { 660 | 661 | init = TO_GeoJson$new() 662 | 663 | testthat::expect_error( init$FeatureCollection(feature_col_dat, stringify = 'TRUE') ) 664 | }) 665 | 666 | 667 | 668 | testthat::test_that("in case that both parameters are valid it returns a GeoJson object", { 669 | 670 | init = TO_GeoJson$new() 671 | 672 | res = init$FeatureCollection(feature_col_dat, stringify = TRUE) 673 | 674 | nams = sum(names(res) %in% c("bbox", "features", "type", "json_dump")) == 4 675 | 676 | TYPE = res$type == "FeatureCollection" 677 | 678 | coords = sum(unlist(lapply(res$features, function(x) is.list(x$geometry) && !length(x$geometry) == 0))) == 2 679 | 680 | testthat::expect_true( sum(c(nams, TYPE, coords)) == 3 ) 681 | }) 682 | 683 | 684 | 685 | #------------------------------ 686 | # Features_2Collection function 687 | #------------------------------ 688 | 689 | 690 | testthat::test_that("in case that the 'Features_files_vec' parameter is not a character string it returns an error", { 691 | 692 | mt = matrix(runif(10), 2, 5) 693 | 694 | testthat::expect_error( Features_2Collection(mt, bbox_vec = NULL) ) 695 | }) 696 | 697 | 698 | testthat::test_that("in case that the 'bbox_vec' parameter is not a numeric vector it returns an error", { 699 | 700 | PATH = paste0(getwd(), path.expand("/file_data/feature_multiple_files")) 701 | 702 | path_files = list.files(PATH, full.names = T) 703 | 704 | mt = matrix(runif(10), 2, 5) 705 | 706 | testthat::expect_error( Features_2Collection(path_files, bbox_vec = mt) ) 707 | }) 708 | 709 | 710 | testthat::test_that("in case that both parameters are valid it returns a named list", { 711 | 712 | PATH = paste0(getwd(), path.expand("/file_data/feature_multiple_files")) 713 | 714 | path_files = list.files(PATH, full.names = T) 715 | 716 | bb = c(-10.01, -10.01, 10.01, 10.01) 717 | 718 | res = Features_2Collection(path_files, bbox_vec = bb) 719 | 720 | tmp = FROM_GeoJson(url_file_string = res) 721 | 722 | nams = sum(names(tmp) %in% c("bbox", "features", "type")) == 3 723 | 724 | obj = tmp$type == "FeatureCollection" 725 | 726 | len = length(tmp$features) == 5 727 | 728 | testthat::expect_true(sum(c(nams, obj, len)) == 3) 729 | }) 730 | 731 | 732 | 733 | #------------------------- 734 | # shiny_from_JSON function 735 | #------------------------- 736 | 737 | 738 | testthat::test_that("in case that the 'input_file' parameter is not a character string it returns an error", { 739 | 740 | mt = matrix(runif(10), 2, 5) 741 | 742 | testthat::expect_error( shiny_from_JSON(mt) ) 743 | }) 744 | 745 | 746 | testthat::test_that("in case that the 'input_file' parameter is a valid path to a file it returns a named list", { 747 | 748 | PATH = paste0(getwd(), path.expand("/file_data/Feature.geojson")) 749 | 750 | res = shiny_from_JSON(PATH) 751 | 752 | nams = sum(names(res) %in% c("bbox", "geometry", "id", "properties", "type")) == 5 753 | 754 | TYPE = res$type == "Feature" 755 | 756 | coords = is.list(res$geometry) && !length(res$geometry) == 0 757 | 758 | testthat::expect_true( sum(c(nams, TYPE, coords)) == 3 ) 759 | }) 760 | 761 | 762 | 763 | #--------------------- 764 | # merge_files function 765 | #--------------------- 766 | 767 | testthat::test_that("in case that the 'INPUT_FOLDER' parameter is not a character string it returns an error", { 768 | 769 | testthat::expect_error( merge_files(INPUT_FOLDER = NULL, OUTPUT_FILE = "/valid/file.json", CONCAT_DELIMITER = "\n", verbose = FALSE) ) 770 | }) 771 | 772 | 773 | testthat::test_that("in case that the 'OUTPUT_FILE' parameter is not a character string it returns an error", { 774 | 775 | testthat::expect_error( merge_files(INPUT_FOLDER = "/valid/path", OUTPUT_FILE = NULL, CONCAT_DELIMITER = "\n", verbose = FALSE) ) 776 | }) 777 | 778 | 779 | testthat::test_that("in case that the 'CONCAT_DELIMITER' parameter is not a character string it returns an error", { 780 | 781 | testthat::expect_error( merge_files(INPUT_FOLDER = "/valid/path", OUTPUT_FILE = "/valid/file.json", CONCAT_DELIMITER = NULL, verbose = FALSE) ) 782 | }) 783 | 784 | 785 | testthat::test_that("in case that the 'verbose' parameter is not a boolean it returns an error", { 786 | 787 | testthat::expect_error( merge_files(INPUT_FOLDER = "/valid/path", OUTPUT_FILE = "/valid/file.json", CONCAT_DELIMITER = "\n", verbose = NULL) ) 788 | }) 789 | 790 | 791 | testthat::test_that("in case that the 'INPUT_FOLDER' parameter does not end in slash it returns an error", { 792 | 793 | testthat::expect_error( merge_files(INPUT_FOLDER = "/valid/path", OUTPUT_FILE = "/valid/file.json", CONCAT_DELIMITER = "\n", verbose = FALSE) ) 794 | }) 795 | 796 | 797 | testthat::test_that("in case that the 'INPUT_FOLDER' parameter does not end in slash it returns an error", { 798 | 799 | PATH_file_exists = paste0(getwd(), path.expand("/file_exists.json")) 800 | 801 | testthat::expect_error( merge_files(INPUT_FOLDER = "/valid/path/", OUTPUT_FILE = PATH_file_exists, CONCAT_DELIMITER = "\n", verbose = FALSE) ) 802 | }) 803 | 804 | 805 | testthat::test_that("in case that the 'OUTPUT_FILE' already exists it returns a warning. Then it writes the content of the folder to a file and finally it removes the file from the directory)", { 806 | 807 | temporary_function = function() { 808 | 809 | PATH_folder_exists = paste0(getwd(), path.expand("/merge_folder/")) 810 | 811 | PATH_file_exists = paste0(getwd(), path.expand("/file_exists.json")) 812 | 813 | file.create(PATH_file_exists, showWarnings = F) 814 | 815 | merge_files(INPUT_FOLDER = PATH_folder_exists, OUTPUT_FILE = PATH_file_exists, CONCAT_DELIMITER = "\n", verbose = FALSE) 816 | 817 | file.remove(PATH_file_exists) 818 | } 819 | 820 | testthat::expect_warning( temporary_function() ) # at the same time it works as testthat::expect_true() 821 | }) 822 | 823 | 824 | #=========================================================================== 825 | 826 | 827 | testthat::test_that("it throws an error in case that the json object includes an invalid json data type ( valid json-objects : 'string', 'number', 'a JSON object', 'array', 'boolean', 'null' )", { 828 | 829 | char_str = '{"invalid_data": NaN, "valid_data": 1}' # NaN returns NULL 830 | 831 | testthat::expect_error( geojsonR:::export_From_JSON(char_str) ) 832 | }) 833 | 834 | 835 | #=========================================================================== 836 | 837 | 838 | #---------------------------------------------------- 839 | # 'save_R_list_Features_2_FeatureCollection' function 840 | #---------------------------------------------------- 841 | 842 | 843 | testthat::test_that("the 'save_R_list_Features_2_FeatureCollection' function works as expected (Be aware that the sample input matrices MUST have 2 columns to resemble lat-lon values, otherwise an error will be raised)", { 844 | 845 | #............................................................................ 846 | # Matrices that are used in the following polygon and multipolygon geometries 847 | #............................................................................ 848 | 849 | DIGITS = 5 850 | set.seed(1) 851 | 852 | mt_20_2_dims = matrix(round(runif(20), digits = DIGITS), nrow = 10, ncol = 2) 853 | mt_8_2_dims = matrix(round(runif(8), digits = DIGITS), nrow = 4, ncol = 2) 854 | 855 | 856 | polygon_WITHOUT_interior = list(type ="Feature", 857 | id = 1L, 858 | properties = list(prop1 = 'polygon-without', prop2 = 1.0234), 859 | geometry = list(type = 'Polygon', 860 | coordinates = mt_20_2_dims)) 861 | 862 | polygon_WITH_interior = list(type ="Feature", 863 | id = 2L, 864 | properties = list(prop1 = 'polygon-with', prop2 = 4.892), 865 | geometry = list(type = 'Polygon', 866 | coordinates = list(list(mt_20_2_dims, 867 | matrix(runif(8), nrow = 4, ncol = 2))))) # a polygon with interior rings is a list of length 1 which includes 2 or more matrices 868 | 869 | multipolygon_WITHOUT_interior = list(type ="Feature", 870 | id = 3L, 871 | properties = list(prop1 = 'multipolygon-without', prop2 = 6.0987), 872 | geometry = list(type = 'MultiPolygon', 873 | coordinates = list(mt_20_2_dims, 874 | mt_20_2_dims))) 875 | 876 | multipolygon_WITH_interior = list(type ="Feature", 877 | id = 4L, 878 | properties = list(prop1 = 'multipolygon-with', prop2 = 9.337), 879 | geometry = list(type = 'MultiPolygon', 880 | coordinates = list(list(mt_20_2_dims, # one or more polygons with interior rings 881 | mt_8_2_dims), 882 | mt_20_2_dims, # one or more polygons without interior rings 883 | mt_20_2_dims))) 884 | list_features = list(polygon_WITHOUT_interior, 885 | polygon_WITH_interior, 886 | multipolygon_WITHOUT_interior, 887 | multipolygon_WITH_interior) 888 | 889 | path_feat_col = tempfile(fileext = '.geojson') 890 | 891 | res = save_R_list_Features_2_FeatureCollection(input_list = list_features, 892 | path_to_file = path_feat_col, 893 | verbose = TRUE) 894 | 895 | res_load = FROM_GeoJson_Schema(url_file_string = path_feat_col) 896 | 897 | prop_names = unlist(lapply(res_load$features, function(x) x$properties$prop1)) 898 | 899 | feat_nams = c("geometry", "id", "properties", "type") 900 | 901 | TOLERANCE = as.numeric(paste(c('0.', paste(rep(0, DIGITS-1), collapse = ''), '1'), collapse = '')) 902 | 903 | cat('\nUsing a tolerance value of: ', TOLERANCE, '\n') 904 | 905 | plg_wo_int = all.equal(polygon_WITHOUT_interior[feat_nams], res_load$features[[which(prop_names == 'polygon-without')]][feat_nams], tol = TOLERANCE) # add the tolerance parameter for differences that might arise due to save and load procedure 906 | plg_with_int = all.equal(polygon_WITH_interior[feat_nams], res_load$features[[which(prop_names == 'polygon-with')]][feat_nams], tol = TOLERANCE) 907 | mlt_plg_wo_int = all.equal(multipolygon_WITHOUT_interior[feat_nams], res_load$features[[which(prop_names == 'multipolygon-without')]][feat_nams], tol = TOLERANCE) 908 | mlt_plg_with_int = all.equal(multipolygon_WITH_interior[feat_nams], res_load$features[[which(prop_names == 'multipolygon-with')]][feat_nams], tol = TOLERANCE) 909 | 910 | verify_plgs = rep(FALSE, 4) 911 | 912 | lst_res = list(plg_wo_int, plg_with_int, mlt_plg_wo_int, mlt_plg_with_int) 913 | vec_nams = c('polygon-without', 'polygon-with', 'multipolygon-without', 'multipolygon-with') 914 | 915 | for (item in 1:length(lst_res)) { 916 | cat('The ', vec_nams[item], ' geometry returned:', lst_res[[item]], '\n') 917 | 918 | if (is.logical(lst_res[[item]])) { 919 | verify_plgs[item] = lst_res[[item]] 920 | } 921 | } 922 | 923 | idx_error = which(!verify_plgs) 924 | if (length(idx_error) > 0) { 925 | cat('The ', vec_nams[idx_error], ' geometry gave an Error! Take a look! See if it is related with the "tolerance" parameter!\n') 926 | } 927 | 928 | cat('\n') 929 | 930 | if (file.exists(path_feat_col)) file.remove(path_feat_col) 931 | 932 | testthat::expect_true( all( verify_plgs ) ) 933 | }) 934 | 935 | 936 | 937 | -------------------------------------------------------------------------------- /tic.R: -------------------------------------------------------------------------------- 1 | # installs dependencies, runs R CMD check, runs covr::codecov() 2 | do_package_checks() 3 | 4 | if (ci_on_ghactions() && ci_has_env("BUILD_PKGDOWN")) { 5 | # creates pkgdown site and pushes to gh-pages branch 6 | # only for the runner with the "BUILD_PKGDOWN" env var set 7 | do_pkgdown() 8 | } 9 | -------------------------------------------------------------------------------- /vignettes/geocoding_nominatim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlampros/geojsonR/99bfb444b7f6b53250c33321d4b4d3c1994c3142/vignettes/geocoding_nominatim.png -------------------------------------------------------------------------------- /vignettes/geocoding_nominatim_reverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlampros/geojsonR/99bfb444b7f6b53250c33321d4b4d3c1994c3142/vignettes/geocoding_nominatim_reverse.png -------------------------------------------------------------------------------- /vignettes/geojson_geocoding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlampros/geojsonR/99bfb444b7f6b53250c33321d4b4d3c1994c3142/vignettes/geojson_geocoding.png -------------------------------------------------------------------------------- /vignettes/the_geojsonR_package.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Processing of GeoJson data in R" 3 | author: "Lampros Mouselimis" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Processing of GeoJson data in R} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | 13 | 14 | "[GeoJSON](https://en.wikipedia.org/wiki/GeoJSON) is an open standard format designed for representing simple geographical features, along with their non-spatial attributes, based on JavaScript Object Notation. The features include points (therefore *addresses* and *locations*), line strings (therefore *streets*, *highways* and *boundaries*), polygons (*countries*, *provinces*, *tracts of land*), and multi-part collections of these types. GeoJSON features need not represent entities of the physical world only; *mobile routing* and *navigation apps*, for example, might describe their service coverage using GeoJSON. The GeoJSON format differs from other GIS standards in that it was written and is maintained not by a formal standards organization, but by an Internet working group of developers."
15 | 16 | **geojsonR** relies mainly on [RFC 7946](https://datatracker.ietf.org/doc/html/rfc7946), which is the new standard specification of the GeoJSON format. Moreover, the package takes advantage of a C++11 JSON library ([json11](https://github.com/dropbox/json11)) to do the encoding *from* and *to* geojson data objects and all functions are exported in R using the *Rcpp* and *RcppArmadillo* packages.
17 | 18 | 19 | The following lines show an [example GeoJson object](https://datatracker.ietf.org/doc/html/rfc7946) (*"feature_collection.geojson"*), 20 | 21 | ```{r, eval = F} 22 | 23 | { 24 | "type": "FeatureCollection", 25 | "features": [{ 26 | 27 | "type": "Feature", 28 | "geometry": { 29 | "type": "Point", 30 | "coordinates": 31 | [102.0, 0.5] 32 | }, 33 | "properties": { 34 | "prop0": "value0" 35 | } 36 | }, { 37 | 38 | "type": "Feature", 39 | "geometry": { 40 | "type": "LineString", 41 | "coordinates": [ 42 | [102.0, 0.0], 43 | [103.0, 1.0], 44 | [104.0, 0.0], 45 | [102.0, 0.0] 46 | ] 47 | }, 48 | "properties": { 49 | "prop0": "value0", 50 | "prop1": 0.0 51 | } 52 | } 53 | ] 54 | } 55 | 56 | ``` 57 |
58 | The purpose of this package was not only the implementation of functions to convert from and to geojson objects, but also the development of web applications based on geojson objects / data, which I'll explain later in the Vignette. 59 | 60 |
61 | 62 | ##### **FROM_GeoJson** 63 | 64 |
65 | 66 | The **FROM_GeoJson** function can take as input a *path to a file*, *a valid url* (beginning from http..) or a *GeoJson object in form of a character string*. The output is a named list and the resulted *coordinates* member is a matrix, as the following code chunks illustrate, 67 |
68 | 69 | ```{r, eval = F} 70 | 71 | # assuming that the data is saved in the previously mentioned "feature_collection.geojson" 72 | 73 | library(geojsonR) 74 | 75 | # INPUT IS A PATH TO A FILE 76 | 77 | file_js = FROM_GeoJson(url_file_string = "feature_collection.geojson") 78 | 79 | file_js 80 | 81 | ``` 82 | 83 | ```{r, eval = F} 84 | 85 | # output : 86 | 87 | 88 | $features 89 | $features[[1]] 90 | $features[[1]]$geometry 91 | $features[[1]]$geometry$type 92 | [1] "Point" 93 | 94 | $features[[1]]$geometry$coordinates 95 | [,1] [,2] 96 | [1,] 102 0.5 97 | 98 | 99 | $features[[1]]$properties 100 | $features[[1]]$properties$prop0 101 | [1] "value0" 102 | 103 | 104 | $features[[1]]$type 105 | [1] "Feature" 106 | 107 | 108 | $features[[2]] 109 | $features[[2]]$geometry 110 | $features[[2]]$geometry$type 111 | [1] "LineString" 112 | 113 | $features[[2]]$geometry$coordinates 114 | [,1] [,2] 115 | [1,] 102 0 116 | [2,] 103 1 117 | [3,] 104 0 118 | [4,] 102 0 119 | 120 | 121 | $features[[2]]$properties 122 | $features[[2]]$properties$prop0 123 | [1] "value0" 124 | 125 | $features[[2]]$properties$prop1 126 | [1] 0 127 | 128 | 129 | $features[[2]]$type 130 | [1] "Feature" 131 | 132 | 133 | 134 | $type 135 | [1] "FeatureCollection" 136 | 137 | ``` 138 |
139 | 140 | 141 | ```{r, eval = F} 142 | 143 | # INPUT IS A GeoJson OBJECT (character string) 144 | 145 | str_js = '{ "type": "MultiPolygon", 146 | "coordinates": [ 147 | [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]], 148 | [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]], 149 | [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]] 150 | ] 151 | }' 152 | 153 | 154 | char_js = FROM_GeoJson(url_file_string = str_js) 155 | 156 | char_js 157 | 158 | ``` 159 | 160 | ```{r, eval = F} 161 | 162 | $type 163 | [1] "MultiPolygon" 164 | 165 | $coordinates 166 | $coordinates[[1]] 167 | [,1] [,2] 168 | [1,] 102 2 169 | [2,] 103 2 170 | [3,] 103 3 171 | [4,] 102 3 172 | [5,] 102 2 173 | 174 | $coordinates[[2]] 175 | $coordinates[[2]][[1]] 176 | [,1] [,2] 177 | [1,] 100 0 178 | [2,] 101 0 179 | [3,] 101 1 180 | [4,] 100 1 181 | [5,] 100 0 182 | 183 | $coordinates[[2]][[2]] 184 | [,1] [,2] 185 | [1,] 100.2 0.2 186 | [2,] 100.8 0.2 187 | [3,] 100.8 0.8 188 | [4,] 100.2 0.8 189 | [5,] 100.2 0.2 190 | 191 | ``` 192 |
193 | 194 | 195 | ```{r, eval = F} 196 | 197 | # INPUT IS A URL (beginning from http..) 198 | 199 | url_path = "https://raw.githubusercontent.com/mlampros/DataSets/master/california.geojson" 200 | 201 | url_js = FROM_GeoJson(url_file_string = url_path) 202 | 203 | str(url_js) 204 | 205 | Warning message: 206 | closing unused connection 3 (https://raw.githubusercontent.com/mlampros/DataSets/master/california.geojson) 207 | 208 | ``` 209 | 210 | ```{r, eval = F} 211 | 212 | List of 4 213 | $ geometry :List of 2 214 | ..$ type : chr "MultiPolygon" 215 | ..$ coordinates:List of 11 216 | .. ..$ : num [1:56, 1:2] -120 -120 -120 -120 -120 ... 217 | .. ..$ : num [1:64, 1:2] -120 -120 -120 -120 -120 ... 218 | .. ..$ : num [1:36, 1:2] -120 -120 -120 -120 -120 ... 219 | .. ..$ : num [1:24, 1:2] -120 -120 -120 -120 -119 ... 220 | .. ..$ : num [1:16, 1:2] -119 -119 -119 -119 -119 ... 221 | .. ..$ : num [1:42, 1:2] -119 -119 -119 -119 -119 ... 222 | .. ..$ : num [1:48, 1:2] -119 -118 -118 -118 -118 ... 223 | .. ..$ : num [1:11, 1:2] -122 -122 -122 -122 -122 ... 224 | .. ..$ : num [1:11, 1:2] -123 -123 -123 -123 -123 ... 225 | .. ..$ : num [1:9, 1:2] -122 -122 -122 -122 -122 ... 226 | .. ..$ : num [1:1154, 1:2] -124 -124 -124 -124 -124 ... 227 | $ id : chr "california" 228 | $ properties:List of 11 229 | ..$ abbreviation: chr "CA" 230 | ..$ area : num 423968 231 | ..$ capital : chr "Sacramento" 232 | ..$ city : chr "Los Angeles" 233 | ..$ group : chr "US States" 234 | ..$ houseseats : num 53 235 | ..$ landarea : num 403466 236 | ..$ name : chr "California" 237 | ..$ population : num 38332521 238 | ..$ statehood : chr "1850-09-09" 239 | ..$ waterarea : num 20502 240 | $ type : chr "Feature" 241 | 242 | ``` 243 |

244 | 245 | ##### **TO_GeoJson** 246 | 247 |
248 | 249 | The **TO_GeoJson** function is an [R6 class](https://cran.r-project.org/package=R6), which takes as input a *vector* (in case of a *Point*) or a *list* (in case of the rest geometry objects) and converts it to a GeoJson object (named list). The output can also include the data in form of a geojson character string if the argument *stringify* is TRUE. The following code chunk shows the output for a polygon with interior rings, 250 | 251 | 252 | ```{r, eval = F} 253 | 254 | init = TO_GeoJson$new() 255 | 256 | polygon_WITH_dat = list(list(c(100, 1.01), c(200, 2.01), c(100, 1.0), c(100, 1.01)), 257 | 258 | list(c(50, 0.5), c(50, 0.8), c(50, 0.9), c(50, 0.5))) 259 | 260 | polygon_with = init$Polygon(polygon_WITH_dat, stringify = TRUE) 261 | 262 | str(polygon_with) 263 | 264 | ``` 265 | 266 | ```{r, eval = F} 267 | 268 | List of 3 269 | $ json_dump : chr "{\"coordinates\": [[[100, 1.01], [200, 2.0099999999999998], [100, 1], [100, 1.01]], [[50, 0.5], [50, 0.80000000000000004], [50,"| __truncated__ 270 | $ type : chr "Polygon" 271 | $ coordinates:List of 2 272 | ..$ :List of 4 273 | .. ..$ : num [1:2] 100 1.01 274 | .. ..$ : num [1:2] 200 2.01 275 | .. ..$ : num [1:2] 100 1 276 | .. ..$ : num [1:2] 100 1.01 277 | ..$ :List of 4 278 | .. ..$ : num [1:2] 50 0.5 279 | .. ..$ : num [1:2] 50 0.8 280 | .. ..$ : num [1:2] 50 0.9 281 | .. ..$ : num [1:2] 50 0.5 282 | 283 | ``` 284 | 285 | ```{r, eval = F} 286 | 287 | # if "stringify = TRUE" 288 | 289 | cat(polygon_with$json_dump) 290 | 291 | {"coordinates": [[[100, 1.01], [200, 2.0099999999999998], [100, 1], [100, 1.01]], [[50, 0.5], [50, 0.80000000000000004], [50, 0.90000000000000002], [50, 0.5]]], "type": "Polygon"} 292 | 293 | ``` 294 |
295 | 296 | The package documentation includes examples on how to build geojson objects for all geometries ( Point, MultiPoint, LineString, MultiLineString, Polygon (with or without interior rings), MultiPolygon, GeometryCollection, Feature and FeatureCollection ). 297 | 298 |

299 | 300 | ##### **Dump_From_GeoJson** 301 | 302 |
303 | 304 | The **Dump_From_GeoJson** function returns a GeoJson character string (GeoJson-dump) for a file or url. The following code excerpt shows the output for the previously mentioned *feature_collection.geojson* file, 305 | 306 |
307 | 308 | ```{r, eval = F} 309 | 310 | dump_js = Dump_From_GeoJson(url_file = "feature_collection.geojson") 311 | 312 | cat(dump_js) 313 | 314 | ``` 315 | 316 | ```{r, eval = F} 317 | 318 | {"features": [{"geometry": {"coordinates": [102, 0.5], "type": "Point"}, "properties": {"prop0": "value0"}, "type": "Feature"}, {"geometry": {"coordinates": [[102, 0], [103, 1], [104, 0], [105, 1]], "type": "LineString"}, "properties": {"prop0": "value0", "prop1": 0}, "type": "Feature"}], "type": "FeatureCollection"} 319 | 320 | ``` 321 | 322 | 323 |
324 | 325 | The same applies to url files beginning from *http..*. 326 | 327 | 328 |

329 | 330 | ##### **Features_2Collection** 331 | 332 |
333 | 334 | The aim of the **Features_2Collection** function is to take a vector of valid path's / url's of *Feature object* files as input and to return a *Feature Collection*. For instance, if I have the following two files ("Feature1.geojson", "Feature2.geojson"), 335 | 336 |
337 | 338 | ```{r, eval = F} 339 | 340 | # "Feature1.geojson" 341 | 342 | { 343 | "type": "Feature", 344 | 345 | "id": 1, 346 | 347 | "bbox": [-10.0, -10.0, 10.0, 10.0], 348 | 349 | "geometry": { 350 | 351 | "type": "Polygon", 352 | 353 | "coordinates": [ 354 | 355 | [ 356 | [-10.0, -10.0], [10.0, -10.0], [10.0, 10.0], [-10.0, -10.0] 357 | ] 358 | ] 359 | }, 360 | 361 | "properties": { 362 | 363 | "prop_1": "addr1", 364 | 365 | "prop_2": 1 366 | } 367 | } 368 | 369 | ``` 370 |
371 | 372 | ```{r, eval = F} 373 | 374 | # "Feature2.geojson" 375 | 376 | { 377 | "type": "Feature", 378 | 379 | "id": 2, 380 | 381 | "bbox": [-10.0, -10.0, 10.0, 10.0], 382 | 383 | "geometry": { 384 | 385 | "type": "Polygon", 386 | 387 | "coordinates": [ 388 | 389 | [ 390 | [-10.0, -10.0], [10.0, -10.0], [10.0, 10.0], [-10.0, -10.0] 391 | ] 392 | ] 393 | }, 394 | 395 | "properties": { 396 | 397 | "prop_1": "addr2", 398 | 399 | "prop_2": 2 400 | } 401 | } 402 | 403 | ``` 404 | 405 |
406 | 407 | then the output of the function will be a Feature Collection dump, 408 | 409 |
410 | 411 | ```{r, eval = F} 412 | 413 | vec_in = c("Feature1.geojson", "Feature2.geojson") 414 | 415 | res_fcol = Features_2Collection(vec_in, bbox_vec = NULL) 416 | 417 | cat(res_fcol) 418 | 419 | ``` 420 | 421 | ```{r, eval = F} 422 | 423 | {"bbox": [], "features": [{"bbox": [-10, -10, 10, 10], "geometry": {"coordinates": [[[-10, -10], [10, -10], [10, 10], [-10, -10]]], "type": "Polygon"}, "id": 1, "properties": {"prop_1": "addr1", "prop_2": 1}, "type": "Feature"}, {"bbox": [-10, -10, 10, 10], "geometry": {"coordinates": [[[-10, -10], [10, -10], [10, 10], [-10, -10]]], "type": "Polygon"}, "id": 2, "properties": {"prop_1": "addr2", "prop_2": 2}, "type": "Feature"}], "type": "FeatureCollection"} 424 | 425 | ``` 426 | 427 | 428 |

429 | 430 | ##### **Web applications using shiny, leaflet, geojsonR and nominatim** 431 | 432 |
433 | 434 | A secondary thought about the *geojsonR* package was to make some of its functions available for applications. I've built some simple [geocoding](https://en.wikipedia.org/wiki/Geocoding) applications taking advantage of shiny, leaflet and Nominatim. The source code of those applications can be found in my [Github repository](https://github.com/mlampros/shiny-server). To open the applications one should either use *from inside an R session*, 435 | 436 | * shiny::runGitHub('shiny-server', 'mlampros', subdir = 'geocoding_geojson') 437 | * shiny::runGitHub('shiny-server', 'mlampros', subdir = 'geocoding_nominatim') 438 | * shiny::runGitHub('shiny-server', 'mlampros', subdir = 'geocoding_nominatim_reverse') 439 | 440 | or follow the web-links using the [shinyapps.io](https://www.shinyapps.io/) service, 441 | 442 | * https://lampros.shinyapps.io/shiny_geocoding/ 443 | * https://lampros.shinyapps.io/shiny_geojson/ 444 | * https://lampros.shinyapps.io/shiny_reverse_geocoding/ 445 | 446 | The latter service is limited to 25 active hours per month (for free accounts), thus if the limit is exceeded then the applications won't be available to the end users. 447 | 448 | I won't go into detail about [shiny](https://shiny.posit.co/) and [leaflet](https://rstudio.github.io/leaflet/), because both packages are well documented and users can find information on how to include them in their projects. On the other hand, "*Nominatim* is a tool to search OpenStreetMap (OSM) data by name and address and to generate synthetic addresses of OSM points (reverse geocoding). It can be found at *nominatim.openstreetmap.org*. Nominatim is also used as one of the sources for the search box on the OpenStreetMap home page. Several companies provide hosted instances of Nominatim that you can query via an API." More information on how to use *Nominatim* can be found at https://wiki.openstreetmap.org/wiki/Nominatim). 449 | 450 |
451 | 452 | ##### **geocoding_geojson** 453 | 454 |
455 | 456 | The first application *geocoding_geojson* takes a valid *GeoJson* file, url (beginning from http..) or a valid json character string as input (*Data Input field*) and a *map_provider* (defaults to OpenStreetMap) and returns a leaflet map. If the *map_provider* is other than OpenStreetMap, then the input parameter should match one of the names(leaflet::providers). More information about the leaflet:providers can be found in the following links: https://rstudio.github.io/leaflet/basemaps.html, https://github.com/leaflet-extras/leaflet-providers and http://leaflet-extras.github.io/leaflet-providers/preview/index.html. The following image shows the output for the [california.geojson](https://raw.githubusercontent.com/mlampros/DataSets/master/california.geojson) file using the *CartoDB.Positron* as *map_provider*, 457 | 458 |
459 | 460 | ![](geojson_geocoding.png) 461 | 462 | 463 |

464 | 465 | ##### **geocoding_nominatim** 466 | 467 |
468 | 469 | The second application *geocoding_nominatim* takes a valid address (using either a streetname, a housenumber, a city, a county, a state, a country, a postalcode and/or a map_provider) and returns all possible locations. The user then can choose one of the resulted locations from the *Output Locations* drop-down menu (if there are more than one) and the output will be a leaflet map with information about the address, longitude and latitude. This type of geocoding is called *forward geocoding* (or *address geocoding*) and is the process of finding an associated latitude and longitude for a given address. The next image shows the results in case that the input to the city field is *Athens*, 470 | 471 |
472 | 473 | ![](geocoding_nominatim.png) 474 | 475 |
476 | 477 | The user can be more specific about the location taking advantage of all or more than one fields. In case that there is no match, the following warning will be returned : *"the nominatim query returns an empty array. Please, modify the initial query"*. 478 | 479 | 480 |

481 | 482 | ##### **geocoding_nominatim_reverse** 483 | 484 |
485 | 486 | The last application *geocoding_nominatim_reverse* takes a valid latitude, longitude, zoom (ranges from 0 to 18) and/or a map_provider and returns an address for these coordinates. A leaflet map will be created with information about the associated address or the nearest address point (*Reverse geocoding*). The following image shows my current location (approximately) using the *Esri.WorldImagery* as *map_provider*, 487 | 488 |
489 | 490 | ![](geocoding_nominatim_reverse.png) 491 | 492 |
493 | 494 | To report any bugs / issues for the shiny-geocoding applications use the following link [https://github.com/mlampros/shiny-server/issues](https://github.com/mlampros/shiny-server/issues). 495 | 496 |
497 | 498 | An updated version of **geojsonR** can be found in my [Github repository](https://github.com/mlampros/geojsonR) and to report bugs/issues for the package use the following link, [https://github.com/mlampros/geojsonR/issues](https://github.com/mlampros/geojsonR/issues). 499 | 500 |
501 | --------------------------------------------------------------------------------