├── .Rbuildignore ├── .github ├── .gitignore └── workflows │ ├── pkgdown.yaml │ └── rhub.yaml ├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── DESCRIPTION ├── LICENSE ├── NAMESPACE ├── NEWS.md ├── R ├── bind.R ├── crop.R ├── geom-transformers.R ├── init.R ├── join.R ├── plot.R ├── sftime.R ├── st_cast.R ├── st_geometry.R ├── st_time.R └── tidyverse.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── appveyor.yml ├── codecov.yml ├── cran-comments.md ├── man ├── bind.Rd ├── geos_binary_ops.Rd ├── geos_combine.Rd ├── is_sortable.Rd ├── plot.sftime.Rd ├── print.sftime.Rd ├── st_as_sftime.Rd ├── st_cast.Rd ├── st_crop.sftime.Rd ├── st_geometry.Rd ├── st_join.Rd ├── st_sftime.Rd ├── st_time.Rd ├── tidyverse.Rd └── transform.sftime.Rd ├── revdep ├── .gitignore ├── README.md ├── cran.md ├── failures.md └── problems.md ├── tic.R └── vignettes ├── .gitignore └── sftime.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | .travis.yml 2 | appveyor.yml 3 | codecov.yml 4 | ^docs$ 5 | tic.R 6 | TODO 7 | README.md 8 | ^packrat/ 9 | ^\.Rprofile$ 10 | ^.*\.Rproj$ 11 | ^\.Rproj\.user$ 12 | LICENSE 13 | ^\.travis\.yml$ 14 | ^appveyor\.yml$ 15 | ^tic\.R$ 16 | ^doc$ 17 | ^cran-comments\.md$ 18 | ^README\.Rmd$ 19 | ^CODE_OF_CONDUCT\.md$ 20 | ^CRAN-RELEASE$ 21 | ^_pkgdown\.yml$ 22 | ^pkgdown$ 23 | ^\.github$ 24 | ^revdep$ 25 | ^Meta$ 26 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown 13 | 14 | jobs: 15 | pkgdown: 16 | runs-on: ubuntu-latest 17 | # Only restrict concurrency for non-PR jobs 18 | concurrency: 19 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 20 | env: 21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 22 | permissions: 23 | contents: write 24 | steps: 25 | - uses: actions/checkout@v3 26 | 27 | - uses: r-lib/actions/setup-pandoc@v2 28 | 29 | - uses: r-lib/actions/setup-r@v2 30 | with: 31 | use-public-rspm: true 32 | 33 | - uses: r-lib/actions/setup-r-dependencies@v2 34 | with: 35 | extra-packages: any::pkgdown, local::. 36 | needs: website 37 | 38 | - name: Build site 39 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 40 | shell: Rscript {0} 41 | 42 | - name: Deploy to GitHub pages 🚀 43 | if: github.event_name != 'pull_request' 44 | uses: JamesIves/github-pages-deploy-action@v4.4.1 45 | with: 46 | clean: false 47 | branch: gh-pages 48 | folder: docs 49 | -------------------------------------------------------------------------------- /.github/workflows/rhub.yaml: -------------------------------------------------------------------------------- 1 | # R-hub's generic GitHub Actions workflow file. It's canonical location is at 2 | # https://github.com/r-hub/actions/blob/v1/workflows/rhub.yaml 3 | # You can update this file to a newer version using the rhub2 package: 4 | # 5 | # rhub::rhub_setup() 6 | # 7 | # It is unlikely that you need to modify this file manually. 8 | 9 | name: R-hub 10 | run-name: "${{ github.event.inputs.id }}: ${{ github.event.inputs.name || format('Manually run by {0}', github.triggering_actor) }}" 11 | 12 | on: 13 | workflow_dispatch: 14 | inputs: 15 | config: 16 | description: 'A comma separated list of R-hub platforms to use.' 17 | type: string 18 | default: 'linux,windows,macos' 19 | name: 20 | description: 'Run name. You can leave this empty now.' 21 | type: string 22 | id: 23 | description: 'Unique ID. You can leave this empty now.' 24 | type: string 25 | 26 | jobs: 27 | 28 | setup: 29 | runs-on: ubuntu-latest 30 | outputs: 31 | containers: ${{ steps.rhub-setup.outputs.containers }} 32 | platforms: ${{ steps.rhub-setup.outputs.platforms }} 33 | 34 | steps: 35 | # NO NEED TO CHECKOUT HERE 36 | - uses: r-hub/actions/setup@v1 37 | with: 38 | config: ${{ github.event.inputs.config }} 39 | id: rhub-setup 40 | 41 | linux-containers: 42 | needs: setup 43 | if: ${{ needs.setup.outputs.containers != '[]' }} 44 | runs-on: ubuntu-latest 45 | name: ${{ matrix.config.label }} 46 | strategy: 47 | fail-fast: false 48 | matrix: 49 | config: ${{ fromJson(needs.setup.outputs.containers) }} 50 | container: 51 | image: ${{ matrix.config.container }} 52 | 53 | steps: 54 | - uses: r-hub/actions/checkout@v1 55 | - uses: r-hub/actions/platform-info@v1 56 | with: 57 | token: ${{ secrets.RHUB_TOKEN }} 58 | job-config: ${{ matrix.config.job-config }} 59 | - uses: r-hub/actions/setup-deps@v1 60 | with: 61 | token: ${{ secrets.RHUB_TOKEN }} 62 | job-config: ${{ matrix.config.job-config }} 63 | - uses: r-hub/actions/run-check@v1 64 | with: 65 | token: ${{ secrets.RHUB_TOKEN }} 66 | job-config: ${{ matrix.config.job-config }} 67 | 68 | other-platforms: 69 | needs: setup 70 | if: ${{ needs.setup.outputs.platforms != '[]' }} 71 | runs-on: ${{ matrix.config.os }} 72 | name: ${{ matrix.config.label }} 73 | strategy: 74 | fail-fast: false 75 | matrix: 76 | config: ${{ fromJson(needs.setup.outputs.platforms) }} 77 | 78 | steps: 79 | - uses: r-hub/actions/checkout@v1 80 | - uses: r-hub/actions/setup-r@v1 81 | with: 82 | job-config: ${{ matrix.config.job-config }} 83 | token: ${{ secrets.RHUB_TOKEN }} 84 | - uses: r-hub/actions/platform-info@v1 85 | with: 86 | token: ${{ secrets.RHUB_TOKEN }} 87 | job-config: ${{ matrix.config.job-config }} 88 | - uses: r-hub/actions/setup-deps@v1 89 | with: 90 | job-config: ${{ matrix.config.job-config }} 91 | token: ${{ secrets.RHUB_TOKEN }} 92 | - uses: r-hub/actions/run-check@v1 93 | with: 94 | job-config: ${{ matrix.config.job-config }} 95 | token: ${{ secrets.RHUB_TOKEN }} 96 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | packrat/lib*/ 2 | .Rproj.user 3 | .Rhistory 4 | .RData 5 | .Ruserdata 6 | sftime.Rproj 7 | inst/doc 8 | docs 9 | /doc/ 10 | /Meta/ 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: r 2 | 3 | r: 4 | - release 5 | - oldrel 6 | - devel 7 | 8 | deploy.skip_cleanup: true 9 | 10 | sudo: required 11 | # dist: trusty 12 | 13 | cache: 14 | - packages 15 | - ccache 16 | 17 | latex: false 18 | 19 | r_github_packages: 20 | - bengraeler/sf 21 | 22 | # - r-spatial/stars 23 | # - r-spatial/s2 24 | # - r-dbi/DBI 25 | # - r-dbi/RPostgres 26 | # - r-lib/covr 27 | # - r-spatial/lwgeom 28 | 29 | addons: 30 | # postgresql: "10" 31 | apt: 32 | sources: 33 | - sourceline: 'ppa:opencpu/jq' 34 | - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' 35 | - sourceline: 'deb http://apt.postgresql.org/pub/repos/apt/ trusty-pgdg main' 36 | key_url: 'https://www.postgresql.org/media/keys/ACCC4CF8.asc' 37 | packages: 38 | - devscripts # checkbashisms 39 | - libprotobuf-dev 40 | - protobuf-compiler 41 | - libv8-3.14-dev 42 | - libjq-dev 43 | - libudunits2-dev 44 | - libproj-dev 45 | - libgeos-dev 46 | - libspatialite-dev 47 | - libgdal-dev 48 | - libjson-c-dev 49 | - libnetcdf-dev 50 | - netcdf-bin 51 | # - postgresql-server-dev-9.6 52 | # - postgis 53 | 54 | before_install: 55 | # install postgis from source, to avoid dependency conflict with GDAL >= 2.0: 56 | #- wget http://download.osgeo.org/postgis/source/postgis-3.0.1.tar.gz 57 | #- (mv postgis* /tmp; cd /tmp; tar xzf postgis-3.0.1.tar.gz) 58 | #- (cd /tmp/postgis-3.0.1 ; ./configure; make; sudo make install) 59 | 60 | # activate liblwgeom by: 61 | ## - sudo ldconfig 62 | # create postgis databases: 63 | #- sudo service postgresql restart 64 | #- createdb postgis 65 | #- psql -d postgis -c "CREATE EXTENSION postgis;" 66 | #- psql -d postgis -c "GRANT CREATE ON DATABASE postgis TO travis" 67 | #- psql -d postgis -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO travis" 68 | #- createdb empty 69 | #- psql -d empty -c "CREATE EXTENSION postgis;" 70 | - R -e 'install.packages("rgdal", repos="http://R-Forge.R-project.org")' 71 | - R -q -e 'install.packages("remotes"); remotes::install_github("bengraeler/sf"); remotes::install_github("ropenscilabs/tic"); tic::prepare_all_stages()' 72 | 73 | after_success: 74 | #- dropdb postgis 75 | #- dropdb empty 76 | #- createdb postgis 77 | #- psql -d postgis -c "CREATE EXTENSION postgis;" 78 | #- psql -d postgis -c "GRANT CREATE ON DATABASE postgis TO travis" 79 | #- psql -d postgis -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO travis" 80 | #- createdb empty 81 | #- psql -d empty -c "CREATE EXTENSION postgis;" 82 | - R -q -e 'covr::codecov(quiet = FALSE)' 83 | 84 | install: R -q -e 'tic::install()' 85 | script: R -q -e 'tic::script()' 86 | before_deploy: R -q -e 'tic::before_deploy()' 87 | deploy: 88 | provider: script 89 | script: R -q -e 'tic::deploy()' 90 | on: 91 | branch: master 92 | condition: 93 | - $TRAVIS_PULL_REQUEST = false 94 | - $TRAVIS_EVENT_TYPE != cron 95 | - $TRAVIS_R_VERSION_STRING = release 96 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards 42 | of acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies 54 | when an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail 56 | address, posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at henning.teickner@uni-muenster.de. 63 | All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series of 85 | actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or permanent 92 | ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within the 112 | community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.0, 118 | available at . 119 | 120 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 121 | enforcement ladder](https://github.com/mozilla/diversity). 122 | 123 | [homepage]: https://www.contributor-covenant.org 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | . Translations are available at . 127 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: sftime 2 | Title: Classes and Methods for Simple Feature Objects that Have a Time Column 3 | Description: Classes and methods for spatial objects that have a registered time 4 | column, in particular for irregular spatiotemporal data. The time column can 5 | be of any type, but needs to be ordinal. Regularly laid out spatiotemporal 6 | data (vector or raster data cubes) are handled by package 'stars'. 7 | Version: 0.3.0.9000 8 | Depends: 9 | sf (>= 1.0.9) 10 | Imports: methods 11 | Suggests: 12 | knitr, 13 | spacetime, 14 | rmarkdown, 15 | dplyr (>= 0.8-3), 16 | trajectories (>= 0.2.2), 17 | stars, 18 | ncmeta, 19 | tidyr, 20 | ggplot2, 21 | magrittr, 22 | sp, 23 | rlang, 24 | vctrs, 25 | spatstat.geom, 26 | spatstat.linnet, 27 | sftrack, 28 | cubble (>= 0.3.0) 29 | Authors@R: 30 | c(person(given = "Henning", 31 | family = "Teickner", 32 | role = c("aut", "cre", "cph"), 33 | email = "henning.teickner@uni-muenster.de", 34 | comment = c(ORCID = "0000-0002-3993-1182")), 35 | person(given = "Edzer", 36 | family = "Pebesma", 37 | role = c("aut", "cph"), 38 | email = "edzer.pebesma@uni-muenster.de", 39 | comment = c(ORCID = "0000-0001-8049-7069")), 40 | person(given = "Benedikt", 41 | family = "Graeler", 42 | role = c("aut", "cph"), 43 | email = "b.graeler@52north.org", 44 | comment = c(ORCID = "0000-0001-5443-4304"))) 45 | License: Apache License 46 | Type: Package 47 | Encoding: UTF-8 48 | VignetteBuilder: knitr 49 | RoxygenNote: 7.2.3 50 | URL: https://r-spatial.github.io/sftime/, https://github.com/r-spatial/sftime 51 | BugReports: https://github.com/r-spatial/sftime/issues/ 52 | Collate: 53 | sftime.R 54 | init.R 55 | join.R 56 | plot.R 57 | st_cast.R 58 | st_geometry.R 59 | st_time.R 60 | tidyverse.R 61 | bind.R 62 | crop.R 63 | geom-transformers.R 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method("$<-",sftime) 4 | S3method("[",sftime) 5 | S3method("[[<-",sftime) 6 | S3method("st_time<-",sf) 7 | S3method("st_time<-",sftime) 8 | S3method(cbind,sftime) 9 | S3method(plot,sftime) 10 | S3method(print,sftime) 11 | S3method(rbind,sftime) 12 | S3method(st_as_sftime,ST) 13 | S3method(st_as_sftime,Track) 14 | S3method(st_as_sftime,Tracks) 15 | S3method(st_as_sftime,TracksCollection) 16 | S3method(st_as_sftime,cubble_df) 17 | S3method(st_as_sftime,data.frame) 18 | S3method(st_as_sftime,lpp) 19 | S3method(st_as_sftime,ppp) 20 | S3method(st_as_sftime,psp) 21 | S3method(st_as_sftime,sf) 22 | S3method(st_as_sftime,sftime) 23 | S3method(st_as_sftime,sftrack) 24 | S3method(st_as_sftime,sftraj) 25 | S3method(st_as_sftime,stars) 26 | S3method(st_cast,sftime) 27 | S3method(st_crop,sftime) 28 | S3method(st_difference,sftime) 29 | S3method(st_drop_geometry,sftime) 30 | S3method(st_filter,sftime) 31 | S3method(st_intersection,sftime) 32 | S3method(st_join,sftime) 33 | S3method(st_sym_difference,sftime) 34 | S3method(st_time,sftime) 35 | S3method(st_union,sftime) 36 | S3method(transform,sftime) 37 | export("st_time<-") 38 | export(is_sortable) 39 | export(st_as_sftime) 40 | export(st_drop_time) 41 | export(st_set_time) 42 | export(st_sftime) 43 | export(st_time) 44 | import(sf) 45 | importFrom(graphics,plot) 46 | importFrom(methods,as) 47 | importFrom(methods,slotNames) 48 | importFrom(utils,methods) 49 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # sftime (development version) 2 | 3 | # sftime 0.3.0 4 | 5 | * Add a dedicated `tidyr::drop_na()` method for `sftime` objects. (See the same recent addition for `sf` objects [#1975](https://github.com/r-spatial/sf/pull/1975/)). 6 | 7 | * Add a dedicated `dplyr::dplyr_reconstruct()` method for `sftime` objects. 8 | Relying on the method for `sf` objects caused erroneously column binding when the second object was a data frame without conflicting column names for the `sf` and time columns. In this case, a `sf` objects was returned, even though an `sftime` object should be returned. See also https://github.com/r-spatial/sf/issues/1958#issuecomment-1181982244. 9 | 10 | * Add methods to convert `sftime` objects from: 11 | + Objects from the `spatstat` package classes (`ppp`, `psp`, `lpp`) 12 | + `sftrack` and `sftraj` objects from the `sftrack` package. 13 | + `cubble_df` objects from the `cubble` package. 14 | 15 | * Bug fix in `st_time<-.sftime`: 16 | + Still contained references to the old `tc`class. 17 | + Did not allow to give the active time column a character vector as value. 18 | 19 | # version 0.2-0 20 | 21 | * initial CRAN submission 22 | -------------------------------------------------------------------------------- /R/bind.R: -------------------------------------------------------------------------------- 1 | #' Bind rows (features) of \code{sftime} objects 2 | #' 3 | #' @name bind 4 | #' @param ... Objects to bind; note that for the \code{rbind} and \code{cbind} 5 | #' methods, all objects have to be of class \code{sftime}; see 6 | #' \code{\link{dotsMethods}}. 7 | #' @param deparse.level An integer value; see \code{\link{rbind}}. 8 | #' @return \code{rbind} combines all \code{sftime} objects in \code{...} 9 | #' row-wise and returns the combined \code{sftime} object. 10 | #' @details Both \code{rbind} and \code{cbind} have non-standard method dispatch 11 | #' (see \link[base]{cbind}): the \code{rbind} or \code{cbind} method for 12 | #' \code{sftime} objects is only called when all arguments to be combined are of 13 | #' class \code{sftime}. 14 | #' @export 15 | #' @examples 16 | #' g1 <- st_sfc(st_point(1:2)) 17 | #' x1 <- st_sftime(a = 3, geometry = g1, time = Sys.time()) 18 | #' 19 | #' g2 <- st_sfc(st_point(c(4, 6))) 20 | #' x2 <- st_sftime(a = 4, geometry = g2, time = Sys.time()) 21 | #' 22 | #' rbind(x1, x2) # works because both tc1 and tc2 have the same class 23 | #' 24 | #' \dontrun{ 25 | #' st_time(x2) <- 1 26 | #' rbind(x1, x2) # error because both tc1 and tc2 do not have the same class 27 | #' } 28 | #' 29 | rbind.sftime <- function(..., deparse.level = 1) { 30 | 31 | dots <- list(...) 32 | dots <- dots[!sapply(dots, is.null)] 33 | stopifnot(vapply(dots, inherits, "sftime", FUN.VALUE = TRUE)) 34 | 35 | tc0 <- class(st_time(dots[[1]])) 36 | if (length(dots) > 1L) { # check all time columns are equal... 37 | equal_tc <- vapply(dots[-1L], function(x) identical(tc0, class(st_time(x))), TRUE) 38 | if (!all(equal_tc)) 39 | stop("Arguments have different time column classes", call. = FALSE) 40 | } 41 | 42 | nr <- sapply(dots, NROW) 43 | tc_column <- if (any(nr > 0)) 44 | attr(dots[[ which(nr > 0)[1] ]], "tc_column") 45 | else 46 | NULL 47 | 48 | st_sftime(do.call(rbind, lapply(dots, function(x) structure(x, class = setdiff(class(x), "sftime")))), time_column_name = tc_column) 49 | 50 | } 51 | 52 | #' Bind columns (variables) of \code{sftime} objects 53 | #' 54 | #' @name bind 55 | #' @param sf_column_name Character value; specifies the active geometry column; 56 | #' passed on to \code{\link{st_sftime}}. 57 | #' @param tc_column_name Character value; specifies active time column; passed 58 | #' on to \code{\link{st_sftime}}. 59 | #' @return \code{cbind} combines all \code{sftime} objects in \code{...} 60 | #' column-wise and returns the combined \code{sftime} object. When called with 61 | #' multiple \code{sftime} objects warns about multiple time and geometry columns 62 | #' present when the time and geometry columns to use are not specified by using 63 | #' arguments \code{tc_column_name} and \code{sf_column_name}; see also 64 | #' \link{st_sftime}. 65 | #' @export 66 | #' @details If you need to \code{cbind} e.g. a \code{data.frame} to an \code{sf}, 67 | #' use \code{\link{data.frame}} directly and use \code{\link{st_sftime}} on its 68 | #' result, or use \code{\link[dplyr:bind]{bind_cols}}; see examples. 69 | #' @examples 70 | #' cbind(x1, x2) 71 | #' 72 | #' if (require(dplyr)) { 73 | #' # returns a data frame because names of sf and time column are modified: 74 | #' dplyr::bind_cols(x1, x2) 75 | #' 76 | #' # returns an sf object because the name of the time column is modified: 77 | #' dplyr::bind_cols(x1, x2 %>% sf::st_drop_geometry()) 78 | #' 79 | #' # returns an sftime object because names of sf and time column are both 80 | #' # preserved: 81 | #' dplyr::bind_cols(x1, x2 %>% st_drop_time() %>% sf::st_drop_geometry()) 82 | #' } 83 | #' 84 | #' df <- data.frame(x = 3) 85 | #' st_sftime(data.frame(x1, df)) 86 | #' 87 | cbind.sftime = function(..., deparse.level = 1, sf_column_name = NULL, tc_column_name = NULL) { 88 | st_sftime(data.frame(...), sf_column_name = sf_column_name, time_column_name = tc_column_name) 89 | } 90 | -------------------------------------------------------------------------------- /R/crop.R: -------------------------------------------------------------------------------- 1 | #' Crop an \code{sftime} object to a specific rectangle 2 | #' 3 | #' @param x An object of class \code{sftime}. 4 | #' @param y A numeric vector with named elements \code{xmin}, \code{ymin}, 5 | #' \code{xmax} and \code{ymax}, or an object of class \code{bbox}, or an object 6 | #' for which there is an \code{\link[sf:st_bbox]{st_bbox}} method to convert it 7 | #' to a \code{bbox} object. 8 | #' @param ... Additional arguments; Ignored. 9 | #' @return \code{x} cropped using \code{y}. 10 | #' @details 11 | #' See \code{\link[sf:st_crop]{st_crop}}. 12 | #' @examples 13 | #' # modified from sf: 14 | #' box <- c(xmin = 0, ymin = 0, xmax = 1, ymax = 1) 15 | #' pol <- sf::st_sfc(sf::st_buffer(sf::st_point(c(0.5, 0.5)), 0.6)) 16 | #' pol_sftime <- st_sftime(a = 1, geom = pol, time = Sys.time() + 1:2 * 1000) 17 | #' 18 | #' pol_sftime_cropped <- sf::st_crop(pol_sftime, sf::st_bbox(box)) 19 | #' 20 | #' class(pol_sftime_cropped) 21 | #' plot(pol_sftime_cropped) 22 | #' @export 23 | st_crop.sftime <- function(x, y, ...) { 24 | reclass_sftime(NextMethod(), time_column_name = attr(x, "time_column")) 25 | } -------------------------------------------------------------------------------- /R/geom-transformers.R: -------------------------------------------------------------------------------- 1 | #' Geometric operations on pairs of simple feature geometry sets (including \code{sftime} objects) 2 | #' 3 | #' @name geos_binary_ops 4 | #' @param x object of class \code{sftime}, \code{sf}, \code{sfc} or \code{sfg}. 5 | #' @param y object of class \code{sftime}, \code{sf}, \code{sfc} or \code{sfg}. 6 | #' @param ... See \code{\link[sf:geos_binary_ops]{geos_binary_ops}}. 7 | #' @return The intersection, difference or symmetric difference between two sets 8 | #' of geometries. 9 | #' The returned object has the same class as that of the first argument 10 | #' (\code{x}) with the non-empty geometries resulting from applying the 11 | #' operation to all geometry pairs in \code{x} and \code{y}. In case \code{x} 12 | #' is of class \code{sf} or \code{sftime}, the matching attributes of the 13 | #' original object(s) are added. The \code{sfc} geometry list-column returned 14 | #' carries an attribute \code{idx}, which is an \code{n}-by-2 matrix with every 15 | #' row the index of the corresponding entries of \code{x} and \code{y}, 16 | #' respectively. 17 | #' 18 | #' @examples 19 | #' g <- st_sfc(st_point(c(1, 2)), st_point(c(1, 3)), st_point(c(2, 3)), 20 | #' st_point(c(2, 1)), st_point(c(3, 1))) 21 | #' tc <- Sys.time() + 1:5 22 | #' x1 <- st_sftime(a = 1:5, g, time = tc) 23 | #' x2 <- st_buffer(x1, dist = 1) 24 | #' 25 | NULL 26 | 27 | #' Intersection 28 | #' @name geos_binary_ops 29 | #' @details \code{st_intersection}: When called with a missing \code{y}, the 30 | #' \code{sftime} method for \code{st_intersection} returns an \code{sftime} 31 | #' object with attributes taken from the contributing feature with lowest index; 32 | #' two fields are added: 33 | #' \describe{ 34 | #' \item{\code{n.overlaps}}{The number of overlapping features in \code{x}.} 35 | #' \item{\code{origins}}{A list-column with indexes of all overlapping 36 | #' features.} 37 | #' } 38 | #' 39 | #' @examples 40 | #' ## intersection 41 | #' 42 | #' # only x provided (no y) 43 | #' plot(st_intersection(x2)) 44 | #' 45 | #' # with arguments x and y provided 46 | #' plot(st_intersection(x2, x1)) 47 | #' 48 | #' @export 49 | st_intersection.sftime <- function(x, y, ...) { 50 | time_column_name <- attr(x, "time_column") 51 | reclass_sftime(NextMethod(), time_column_name = time_column_name) 52 | } 53 | 54 | #' Difference 55 | #' @name geos_binary_ops 56 | #' @details \code{st_difference}: When \code{st_difference} is called with a 57 | #' single argument, overlapping areas are erased from geometries that are 58 | #' indexed at greater numbers in the argument to \code{x}; geometries that are 59 | #' empty or contained fully inside geometries with higher priority are removed 60 | #' entirely. 61 | #' 62 | #' @examples 63 | #' ## difference 64 | #' 65 | #' # only x provided (no y) 66 | #' plot(st_difference(x2)) 67 | #' 68 | #' # with arguments x and y provided 69 | #' plot(st_difference(x2, x1)) 70 | #' 71 | #' @export 72 | st_difference.sftime <- function(x, y, ...) { 73 | time_column_name <- attr(x, "time_column") 74 | reclass_sftime(NextMethod(), time_column_name = time_column_name) 75 | } 76 | 77 | #' @name geos_binary_ops 78 | #' @examples 79 | #' ## symmetric difference 80 | #' plot(st_sym_difference(x1, x2)) 81 | #' 82 | #' @export 83 | st_sym_difference.sftime <- function(x, y, ...) { 84 | time_column_name <- attr(x, "time_column") 85 | reclass_sftime(NextMethod(), time_column_name = time_column_name) 86 | } 87 | 88 | #' Combine or union feature geometries (including \code{sftime} objects) 89 | #' 90 | #' @name geos_combine 91 | #' @param x An object of class \code{sftime}, \code{sf}, \code{sfc} or 92 | #' \code{sfg}. 93 | #' @param y An object of class \code{sftime}, \code{sf}, \code{sfc} or 94 | #' \code{sfg} (optional). 95 | #' @param by_feature See \code{\link[sf:geos_combine]{geos_combine}}. 96 | #' @param is_coverage See \code{\link[sf:geos_combine]{geos_combine}}. 97 | #' @param ... See \code{\link[sf:geos_combine]{geos_combine}}. 98 | #' @return If \code{y} is missing, \code{st_union(x)} returns a single geometry 99 | #' with resolved boundaries, else the geometries for all unioned pairs of 100 | #' \code{x[i]} and \code{y[j]}. 101 | #' @details 102 | #' See \code{\link[sf:geos_combine]{geos_combine}}. 103 | #' 104 | #' @examples 105 | #' # union simple features in an sftime object 106 | #' g <- st_sfc(st_point(c(1, 2)), st_point(c(1, 3)), st_point(c(2, 3)), 107 | #' st_point(c(2, 1)), st_point(c(3, 1))) 108 | #' tc <- Sys.time() + 1:5 109 | #' x <- st_sftime(a = 1:5, g, time = tc) 110 | #' 111 | #' # only x provided (no y) 112 | #' plot(st_union(st_buffer(x, dist = 1))) 113 | #' 114 | #' # with arguments x and y provided 115 | #' plot(st_union(st_buffer(x, dist = 1), st_buffer(x, dist = 0.5)), "a") 116 | #' 117 | #' @export 118 | st_union.sftime <- function(x, y, ..., by_feature = FALSE, is_coverage = FALSE) { 119 | time_column_name <- attr(x, "time_column") 120 | reclass_sftime(NextMethod(), time_column_name = time_column_name) 121 | } 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /R/init.R: -------------------------------------------------------------------------------- 1 | #' @import sf 2 | NULL 3 | 4 | # from: https://github.com/cran/sf/blob/master/R/tidyverse.R: 5 | # from: https://github.com/tidyverse/hms/blob/master/R/zzz.R 6 | # Thu Apr 19 10:53:24 CEST 2018 7 | register_s3_method <- function(pkg, generic, class, fun = NULL) { 8 | stopifnot(is.character(pkg), length(pkg) == 1) 9 | stopifnot(is.character(generic), length(generic) == 1) 10 | stopifnot(is.character(class), length(class) == 1) 11 | 12 | if (is.null(fun)) { 13 | fun <- get(paste0(generic, ".", class), envir = parent.frame()) 14 | } else { 15 | stopifnot(is.function(fun)) 16 | } 17 | 18 | if (pkg %in% loadedNamespaces()) { 19 | registerS3method(generic, class, fun, envir = asNamespace(pkg)) 20 | } 21 | 22 | # Always register hook in case package is later unloaded & reloaded 23 | setHook( 24 | packageEvent(pkg, "onLoad"), 25 | function(...) { 26 | registerS3method(generic, class, fun, envir = asNamespace(pkg)) 27 | } 28 | ) 29 | } 30 | 31 | register_all_s3_methods <- function() { 32 | 33 | # tidyverse joins 34 | register_s3_method("dplyr", "inner_join", "sftime") 35 | register_s3_method("dplyr", "left_join", "sftime") 36 | register_s3_method("dplyr", "right_join", "sftime") 37 | register_s3_method("dplyr", "full_join", "sftime") 38 | register_s3_method("dplyr", "semi_join", "sftime") 39 | register_s3_method("dplyr", "anti_join", "sftime") 40 | 41 | register_s3_method("dplyr", "filter", "sftime") 42 | register_s3_method("dplyr", "arrange", "sftime") 43 | register_s3_method("dplyr", "distinct", "sftime") 44 | register_s3_method("dplyr", "group_by", "sftime") 45 | register_s3_method("dplyr", "mutate", "sftime") 46 | register_s3_method("dplyr", "dplyr_reconstruct", "sftime") 47 | register_s3_method("dplyr", "rename", "sftime") 48 | register_s3_method("dplyr", "rowwise", "sftime") 49 | register_s3_method("dplyr", "sample_frac", "sftime") 50 | register_s3_method("dplyr", "sample_n", "sftime") 51 | register_s3_method("dplyr", "select", "sftime") 52 | register_s3_method("dplyr", "slice", "sftime") 53 | register_s3_method("dplyr", "summarise", "sftime") 54 | register_s3_method("dplyr", "summarize", "sftime") 55 | register_s3_method("dplyr", "transmute", "sftime") 56 | register_s3_method("dplyr", "ungroup", "sftime") 57 | register_s3_method("tidyr", "gather", "sftime") 58 | register_s3_method("tidyr", "pivot_longer", "sftime") 59 | register_s3_method("tidyr", "spread", "sftime") 60 | register_s3_method("tidyr", "nest", "sftime") 61 | register_s3_method("tidyr", "separate", "sftime") 62 | register_s3_method("tidyr", "separate_rows", "sftime") 63 | register_s3_method("tidyr", "unite", "sftime") 64 | register_s3_method("tidyr", "unnest", "sftime") 65 | register_s3_method("tidyr", "drop_na", "sftime") 66 | 67 | } 68 | 69 | .onLoad <- function(libname, pkgname) { 70 | 71 | register_all_s3_methods() 72 | 73 | } -------------------------------------------------------------------------------- /R/join.R: -------------------------------------------------------------------------------- 1 | #' Helper function to adjust class and attributes of \code{sftime} objects when joining 2 | #' 3 | #' @param x An object to be reclassed to the \code{\link[=st_sftime]{sftime}} 4 | #' class. 5 | #' @param time_colmn_name A character value; name of the original active time 6 | #' column in \code{x} before joining. 7 | #' @param suffix_x A character value representing the suffix to add to the name 8 | #' of the time column in the \code{time_column} attribute when name repair 9 | #' during joining changed the name of the time column. 10 | #' 11 | #' @return \code{x} as \code{sftime} object with adjusted \code{time_column} 12 | #' attribute. 13 | #' 14 | #' @keywords internal 15 | #' @noRd 16 | sftime_join <- function(x, time_column_name, suffix_x = ".x") { 17 | 18 | if (!(time_column_name %in% names(x))) { 19 | time_column_name <- paste0(time_column_name, suffix_x) 20 | stopifnot(time_column_name %in% names(x)) 21 | } 22 | 23 | st_as_sftime(x, time_column_name = time_column_name) 24 | } 25 | 26 | ## Tidyverse joins (see also tidyverse.R) 27 | 28 | #' @name tidyverse 29 | #' @examples 30 | #' g1 <- st_sfc(st_point(1:2), st_point(c(5, 8)), st_point(c(2, 9))) 31 | #' x1 <- st_sftime(a = 1:3, geometry = g1, time = Sys.time()) 32 | #' 33 | #' g2 <- st_sfc(st_point(c(4, 6)), st_point(c(4, 6)), st_point(c(4, 6))) 34 | #' x2 <- st_sftime(a = 2:4, geometry = g2, time = Sys.time()) 35 | #' 36 | #' library(dplyr) 37 | #' 38 | #' ## inner_join 39 | #' inner_join(x1, as.data.frame(x2), by = "a") # note: the active time column is 40 | #' # time.x and the active geometry column geometry.x 41 | #' 42 | #' inner_join(x2, as.data.frame(x1), by = "a") 43 | #' 44 | inner_join.sftime <- function(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...) { 45 | sftime_join(NextMethod(), time_column_name = attr(x, "time_column"), suffix_x = suffix[[1]]) 46 | } 47 | 48 | #' @name tidyverse 49 | #' @examples 50 | #' ## left_join 51 | #' left_join(x1, as.data.frame(x2), by = "a") 52 | #' 53 | #' left_join(x2, as.data.frame(x1), by = "a") 54 | #' 55 | left_join.sftime <- function(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...) { 56 | sftime_join(NextMethod(), time_column_name = attr(x, "time_column"), suffix_x = suffix[[1]]) 57 | } 58 | 59 | #' @name tidyverse 60 | #' @examples 61 | #' ## right_join 62 | #' right_join(x1, as.data.frame(x2), by = "a") 63 | #' 64 | #' right_join(x2, as.data.frame(x1), by = "a") 65 | #' 66 | right_join.sftime <- function(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...) { 67 | sftime_join(NextMethod(), time_column_name = attr(x, "time_column"), suffix_x = suffix[[1]]) 68 | } 69 | 70 | #' @name tidyverse 71 | #' @examples 72 | #' ## full_join 73 | #' full_join(x1, as.data.frame(x2), by = "a") 74 | #' 75 | #' full_join(x2, as.data.frame(x1), by = "a") 76 | #' 77 | full_join.sftime <- function(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...) { 78 | sftime_join(NextMethod(), time_column_name = attr(x, "time_column"), suffix_x = suffix[[1]]) 79 | } 80 | 81 | #' @name tidyverse 82 | #' @examples 83 | #' ## semi_join 84 | #' semi_join(x1, as.data.frame(x2), by = "a") 85 | #' 86 | #' semi_join(x2, as.data.frame(x1), by = "a") 87 | #' 88 | semi_join.sftime <- function(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...) { 89 | sftime_join(NextMethod(), time_column_name = attr(x, "time_column"), suffix_x = suffix[[1]]) 90 | } 91 | 92 | #' @name tidyverse 93 | #' @examples 94 | #' ## anti_join 95 | #' anti_join(x1, as.data.frame(x2), by = "a") 96 | #' 97 | #' anti_join(x2, as.data.frame(x1), by = "a") 98 | #' 99 | anti_join.sftime <- function(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...) { 100 | sftime_join(NextMethod(), time_column_name = attr(x, "time_column"), suffix_x = suffix[[1]]) 101 | } 102 | 103 | 104 | #' Spatial join, spatial filter for \code{sftime} objects 105 | #' 106 | #' @name st_join 107 | #' @param x An object of class \code{sftime} or \code{sf}. 108 | #' @param y An object of class \code{sftime} or \code{sf}. 109 | #' @param join A geometry predicate function with the same profile as 110 | #' \code{\link[sf:geos_binary_pred]{st_intersects}}; see details. 111 | #' @inheritParams sf::st_join 112 | #' @return An object of class \code{sftime}, joined based on geometry. 113 | #' @details Alternative values for argument \code{join} are: 114 | #' \itemize{ 115 | #' \item \link[sf:geos_binary_pred]{st_contains_properly} 116 | #' \item \link[sf:geos_binary_pred]{st_contains} 117 | #' \item \link[sf:geos_binary_pred]{st_covered_by} 118 | #' \item \link[sf:geos_binary_pred]{st_covers} 119 | #' \item \link[sf:geos_binary_pred]{st_crosses} 120 | #' \item \link[sf:geos_binary_pred]{st_disjoint} 121 | #' \item \link[sf:geos_binary_pred]{st_equals_exact} 122 | #' \item \link[sf:geos_binary_pred]{st_equals} 123 | #' \item \link[sf:geos_binary_pred]{st_is_within_distance} 124 | #' \item \link[sf:geos_binary_pred]{st_nearest_feature} 125 | #' \item \link[sf:geos_binary_pred]{st_overlaps} 126 | #' \item \link[sf:geos_binary_pred]{st_touches} 127 | #' \item \link[sf:geos_binary_pred]{st_within} 128 | #' \item any user-defined function of the same profile as the above 129 | #' } 130 | #' A left join returns all records of the \code{x} object with \code{y} fields 131 | #' for non-matched records filled with \code{NA} values; an inner join returns 132 | #' only records that spatially match. 133 | #' 134 | #' @examples 135 | #' g1 <- st_sfc(st_point(c(1,1)), st_point(c(2,2)), st_point(c(3,3))) 136 | #' x1 <- st_sftime(a = 1:3, geometry = g1, time = Sys.time()) 137 | #' 138 | #' g2 <- st_sfc(st_point(c(10,10)), st_point(c(2,2)), st_point(c(2,2)), st_point(c(3,3))) 139 | #' x2 <- st_sftime(a = 11:14, geometry = g2, time = Sys.time()) 140 | #' 141 | #' ## st_join 142 | #' 143 | #' # left spatial join with st_intersects 144 | #' st_join(x1, x2) 145 | #' 146 | #' # inner spatial join with st_intersects 147 | #' st_join(x1, x2, left = FALSE) 148 | #' 149 | #' @export 150 | st_join.sftime <- function(x, y, join = st_intersects, ..., suffix = c(".x", ".y"), left = TRUE, largest = FALSE) { 151 | sftime_join(NextMethod(), time_column_name = attr(x, "time_column"), suffix_x = suffix[[1]]) 152 | } 153 | 154 | #' @name st_join 155 | #' @param .predicate A geometry predicate function with the same profile as 156 | #' \code{\link[sf:geos_binary_pred]{st_intersects}}; see details. 157 | #' @examples 158 | #' ## st_filter 159 | #' 160 | #' st_filter(x1, x2) 161 | #' st_filter(x2, x1) 162 | #' 163 | #' @export 164 | st_filter.sftime <- function(x, y, ..., .predicate = st_intersects) { 165 | reclass_sftime(NextMethod(), time_column_name = attr(x, "time_column")) 166 | } -------------------------------------------------------------------------------- /R/plot.R: -------------------------------------------------------------------------------- 1 | #' Plots an \code{sftime} object 2 | #' 3 | #' \code{plot.sftime} 4 | #' 5 | #' @aliases plot 6 | #' @param x The \code{\link[=st_sftime]{sftime}} object to be plotted. 7 | #' @param y A character value; The variable name to be plotted; if missing, the 8 | #' first variable is plotted. 9 | #' @param ... Additional arguments; Passed on to \code{\link[sf:plot]{plot.sf}}. 10 | #' @param number A numeric value; The number of panels to be plotted, cannot be 11 | #' larger than the number of timestamps; ignored when \code{tcuts} is provided. 12 | #' @param tcuts predefined temporal ranges assigned to each map; if missing, 13 | #' will be determined as equal spans according to \code{number}. 14 | #' 15 | #' @importFrom graphics plot 16 | #' 17 | #' @return Returns \code{NULL} and creates as side effect a plot for \code{x}. 18 | #' @examples 19 | #' set.seed(123) 20 | #' coords <- matrix(runif(100), ncol = 2) 21 | #' g <- st_sfc(lapply(1:50, function(i) st_point(coords[i, ]) )) 22 | #' sft <- st_sftime(a = 1:50, g, time = as.POSIXct("2020-09-01 00:00:00") + 0:49 * 3600 * 6) 23 | #' 24 | #' plot(sft) 25 | #' 26 | #' @export 27 | plot.sftime <- function(x, y, ..., number = 6, tcuts) { 28 | 29 | if (missing(y)) 30 | y <- colnames(x)[[1]] 31 | 32 | stopifnot(y %in% colnames(x)) 33 | 34 | ts <- st_time(x) 35 | if(any(is.na(ts))) { 36 | message("[INFO] there are ", sum(is.na(ts)), " `NA` values in the active time column of `x`. These rows are dropped.") 37 | } 38 | 39 | x <- x[!is.na(ts), ] 40 | ts <- st_time(x) 41 | 42 | if (missing(tcuts)) { 43 | ts_ord <- order(ts) 44 | ts_fac <- tryCatch(as.factor(ts[ts_ord]), error = function(e) e) 45 | if (inherits(ts_fac, "error")) { 46 | ts_fac <- 47 | factor( 48 | as.character(ts[ts_ord]), 49 | levels = unique(as.character(ts[ts_ord])), 50 | ordered = TRUE 51 | ) 52 | } 53 | 54 | ts_nlv <- length(levels(ts_fac)) 55 | 56 | if (number > ts_nlv) { 57 | number <- ts_nlv 58 | message("[INFO] Fewer time stamps in the data than asked for; argument 'number' set to: ", ts_nlv) 59 | } 60 | 61 | tcuts <- seq(1, ts_nlv, length.out = number + 1) 62 | 63 | timeclass <- findInterval(as.numeric(ts_fac), tcuts, rightmost.closed = TRUE) 64 | } else { 65 | number <- length(tcuts) - 1 66 | timeclass <- findInterval(ts, tcuts, rightmost.closed = TRUE) 67 | } 68 | d_ord <- as.data.frame(x)[order(ts), y, drop = FALSE] 69 | 70 | data <- d_ord 71 | if (number > 1) { 72 | for (i in 2:number) { 73 | data <- cbind(data, d_ord[, 1]) 74 | data[timeclass != i, i] = NA 75 | if (i == number) 76 | data[timeclass != 1, 1] <- NA # deal with first time class 77 | } 78 | } 79 | 80 | names(data) <- ts_fac[!duplicated(timeclass)] 81 | d <- sf::st_sf(data, geometry = sf::st_geometry(x)) 82 | 83 | plot(d, ...) 84 | 85 | NULL 86 | } -------------------------------------------------------------------------------- /R/sftime.R: -------------------------------------------------------------------------------- 1 | #### construction #### 2 | 3 | #' Checks whether a vector or list is sortable 4 | #' 5 | #' Checks whether a vector or list is sortable. This is the condition for a 6 | #' vector to be usable as time column in a \code{sftime} object. 7 | #' 8 | #' @name is_sortable 9 | #' @param x The object to check. 10 | #' @return \code{TRUE} if \code{x} passes the check, else \code{FALSE}. 11 | #' @keywords internal 12 | #' 13 | #' @details Checks whether the provided object can be handled by 14 | #' \code{\link{order}}. A couple of basic types are whitelisted. However, custom 15 | #' types can be defined when they provide a dedicated generic to \link{xtfrm}. 16 | #' Note that a \code{list} can only be sorted with \link{atomic} values. See the 17 | #' examples below for a template. 18 | #' 19 | #' @examples 20 | #' x <- Sys.time() + 5:1 * 3600 * 24 21 | #' sort(x) 22 | #' is_sortable(x) 23 | #' 24 | #' @importFrom utils methods 25 | #' @export 26 | is_sortable <- function(x) { 27 | # can x be sorted? 28 | # sort.default checks 'is.object(x)' and uses 'order' to subset and sort the object 29 | # lists and vectors are no objects, sort then uses sort.int which can only handle atomic values 30 | 31 | # Examples: 32 | # x <- Sys.time() + 5:1 * 3600*24 33 | # x <- yearmon(2020+c(5:0)/12) 34 | # x <- yearqtr(2020+c(5:0)/4) 35 | # x <- factor(LETTERS[sample(26, replace = T)], levels=LETTERS[sample(26)]) 36 | # sort(x) 37 | # order(x) 38 | # class(x) 39 | any(vapply(class(x), function(y) y %in% c("integer", "numeric", "POSIXct", "POSIXlt", "Date", "yearmon", "yearqtr", "factor"), TRUE)) || # have a list of wellknown exceptions 40 | any(vapply(class(x), function(y) paste("xtfrm", y, sep=".") %in% methods(class = y), TRUE)) # check for function 'xtfrm.[CLASSNAME]' which is used by 'order' which in turn is used by sort.default 41 | 42 | } 43 | 44 | #' Construct an \code{sftime} object from all its components 45 | #' 46 | #' @param ... Column elements to be binded into an \code{sftime} object or a 47 | #' single \code{list} or \code{data.frame} with such columns. At least one of 48 | #' these columns shall be a geometry list-column of class \code{sfc} and one 49 | #' shall be a time column (to be specified with \code{time_column_name}). 50 | #' @param crs Coordinate reference system, something suitable as input to 51 | #' \code{\link[sf]{st_crs}}. 52 | #' @param agr A character vector; see details below. 53 | #' @param row.names row.names for the created \code{sf} object. 54 | #' @param stringsAsFactors A logical value; see 55 | #' \code{\link[sf]{st_read}}. 56 | #' @param precision A numeric value; see 57 | #' \code{\link[sf]{st_as_binary}}. 58 | #' @param sf_column_name A character value; name of the active list-column with 59 | #' simple feature geometries; in case there is more than one and 60 | #' \code{sf_column_name} is \code{NULL}, the first one is taken. 61 | #' @param time_column_name A character value; name of the active 62 | #' time column. In case \code{time_column_name} is \code{NULL}, the first 63 | #' \code{\link{POSIXct}} column is taken. If there is no \code{POSIXct} column, 64 | #' the first \code{\link{Date}} column is taken. 65 | #' @param sfc_last A logical value; if \code{TRUE}, \code{sfc} columns are 66 | #' always put last, otherwise column order is left unmodified. 67 | #' @param time_column_last A logical value; if \code{TRUE}, the active time column is 68 | #' always put last, otherwise column order is left unmodified. If both \code{sfc_last} 69 | #' and \code{time_column_last} are \code{TRUE}, the active time column is put last. 70 | #' @param check_ring_dir A logical value; see \code{\link[sf]{st_read}}. 71 | #' 72 | #' @return \code{st_sftime}: An object of class \code{sftime}. 73 | #' @examples 74 | #' ## construction with an sfc object 75 | #' library(sf) 76 | #' g <- st_sfc(st_point(1:2)) 77 | #' tc <- Sys.time() 78 | #' st_sftime(a = 3, g, time = tc) 79 | #' 80 | #' ## construction with an sf object 81 | #' \dontrun{ 82 | #' st_sftime(st_sf(a = 3, g), time = tc) 83 | #' # error, because if ... contains a data.frame-like object, no other objects 84 | #' # may be passed through ... . Instead, add the time column before. 85 | #' } 86 | #' 87 | #' st_sftime(st_sf(a = 3, g, time = tc)) 88 | #' 89 | #' @export 90 | st_sftime <- function(..., 91 | agr = sf::NA_agr_, 92 | row.names, 93 | stringsAsFactors = TRUE, 94 | crs, 95 | precision, 96 | sf_column_name = NULL, 97 | time_column_name = NULL, 98 | check_ring_dir = FALSE, 99 | sfc_last = TRUE, 100 | time_column_last = TRUE) { 101 | 102 | # checks 103 | stopifnot(is.null(time_column_name) || (is.character(time_column_name) && length(time_column_name) == 1)) 104 | stopifnot(is.logical(time_column_last) && length(time_column_last) == 1) 105 | 106 | # pass to sf::st_sf to get sf object 107 | x <- list(...) 108 | res <- sf::st_sf(..., 109 | agr = agr, 110 | row.names = row.names, 111 | stringsAsFactors = stringsAsFactors, 112 | crs = crs, 113 | precision = precision, 114 | sf_column_name = sf_column_name, 115 | sfc_last = sfc_last) 116 | 117 | # get info on active time column (modified from sf) 118 | if(!is.null(time_column_name)) { # time column manually specified 119 | 120 | stopifnot(time_column_name %in% colnames(res)) 121 | stopifnot(is_sortable(res[[time_column_name]])) 122 | res_time_column <- match(time_column_name, colnames(res)) 123 | res_time_column_name <- time_column_name 124 | 125 | } else { #search for POSIXct and Date columns 126 | 127 | # search time column(s) 128 | all_time_column_names <- NULL 129 | all_time_columns <- vapply(res, function(x) inherits(x, "POSIXct"), TRUE) 130 | if(!any(all_time_columns)) { 131 | all_time_columns <- vapply(res, function(x) inherits(x, "Date"), TRUE) 132 | } 133 | if(!any(all_time_columns)) stop("No time column found.") 134 | all_time_columns <- which(unlist(all_time_columns)) 135 | 136 | res_time_column <- all_time_columns[[1L]] 137 | res_time_column_name <- names(all_time_columns)[[1L]] 138 | } 139 | 140 | # sort time column 141 | if(time_column_last) { 142 | res_only_time_column <- sf::st_drop_geometry(res[, res_time_column])[, 1, drop = TRUE] 143 | res <- res[, -res_time_column] 144 | res[, res_time_column_name] <- res_only_time_column 145 | res <- sf::st_sf(res, 146 | agr = agr, 147 | row.names = row.names, 148 | stringsAsFactors = stringsAsFactors, 149 | crs = crs, 150 | precision = precision, 151 | sf_column_name = sf_column_name, 152 | sfc_last = FALSE) 153 | } 154 | 155 | # add attributes 156 | attr(res, "time_column") <- res_time_column_name 157 | if(!inherits(res, "sftime")) 158 | class(res) <- c("sftime", class(res)) 159 | 160 | res 161 | } 162 | 163 | #' Helper function for reclassing \code{sftime} objects 164 | #' 165 | #' Reclasses \code{sftime} objects to the correct new class after modification. 166 | #' Checks if the \code{sftime} object (the active time column) gets invalidated. 167 | #' If so, the \code{sftime} class is dropped. If not, the object is reclassed to 168 | #' an \code{sftime} object. 169 | #' 170 | #' @param x An object to be reclassed to the \code{\link[=st_sftime]{sftime}} class. 171 | #' @param time_colmn_name A character value; name of the active time column. 172 | #' @return \code{x} as \code{sftime} object if the column indicated by 173 | #' \code{time_colmn_name} is a valid time column (\code{\link{is_sortable}}) and 174 | #' \code{x} without \code{time_column} attribute if not. 175 | #' 176 | #' @keywords internal 177 | #' @noRd 178 | reclass_sftime <- function(x, time_column_name) { 179 | 180 | if(! time_column_name %in% colnames(x) || ! inherits(x, "sf")) { 181 | structure(x, class = setdiff(class(x), "sftime"), time_column = NULL) 182 | } else { 183 | structure(x, class = c("sftime", setdiff(class(x), "sftime")), time_column = time_column_name) 184 | } 185 | 186 | } 187 | 188 | #### subsetting #### 189 | 190 | #' @name st_sftime 191 | #' @param x An object of class \code{sf}. 192 | #' @param i Record selection, see \link{[.data.frame} 193 | #' @param j Variable selection, see \link{[.data.frame} 194 | #' @param drop A logical value, default \code{FALSE}; if \code{TRUE} drop the 195 | #' geometry column and return a \code{data.frame}, else make the geometry sticky 196 | #' and return an \code{sf} object. 197 | #' @param op A function; geometrical binary predicate function to apply when 198 | #' \code{i} is a simple feature object. 199 | #' @details See also \link{[.data.frame}; for \code{[.sftime} \code{...} 200 | #' arguments are passed to \code{op}. 201 | #' @return Returned objects for subsetting functions: \code{[.sf} will return a 202 | #' \code{data.frame} or vector if the geometry column (of class \code{sfc}) is 203 | #' dropped (\code{drop=TRUE}), an \code{sfc} object if only the geometry column 204 | #' is selected, and otherwise return an \code{sftime} object. 205 | #' @examples 206 | #' ## Subsetting 207 | #' g <- st_sfc(st_point(c(1, 2)), st_point(c(1, 3)), st_point(c(2, 3)), 208 | #' st_point(c(2, 1)), st_point(c(3, 1))) 209 | #' tc <- Sys.time() + 1:5 210 | #' x <- st_sftime(a = 1:5, g, time = tc) 211 | #' 212 | #' # rows 213 | #' x[1, ] 214 | #' class(x[1, ]) 215 | #' 216 | #' x[x$a < 3, ] 217 | #' class(x[x$a < 3, ]) 218 | #' 219 | #' # columns 220 | #' x[, 1] 221 | #' class(x[, 1]) # drops time column as for ordinary data.frame subsetting, 222 | #' # keeps geometry column of sf object 223 | #' 224 | #' x[, 3] 225 | #' class(x[, 3]) # keeps time column because it is explicitly selected, 226 | #' # keeps geometry column of sf object, returns an sftime object 227 | #' 228 | #' x[, 3, drop = TRUE] 229 | #' class(x[, 3, drop = TRUE]) # if the geometry column is dropped, not only the 230 | #' # sf class is dropped, but also the sftime class 231 | #' 232 | #' x["a"] 233 | #' class(x["a"]) # Time columns are not sticky: If a column is selected by a 234 | #' # character vector and this does not contain the active time column, the time 235 | #' # column is dropped. 236 | #' 237 | #' x[c("a", "time")] 238 | #' class(x[c("a", "time")]) # keeps the time column 239 | #' 240 | #' # with sf or sftime object 241 | #' pol = st_sfc(st_polygon(list(cbind(c(0,2,2,0,0),c(0,0,2,2,0))))) 242 | #' h = st_sf(r = 5, pol) 243 | #' 244 | #' x[h, ] 245 | #' class(x[h, ]) # returns sftime object 246 | #' 247 | #' h[x, ] 248 | #' class(h[x, ]) # returns sf object 249 | #' 250 | #' @export 251 | "[.sftime" <- function(x, i, j, ..., drop = FALSE, op = sf::st_intersects) { 252 | 253 | # retain info on time column 254 | time_column <- attr(x, "time_column") 255 | 256 | # perform subsetting for sf object 257 | if((!missing(j) && !drop && ((is.character(j) && any(j == time_column)) || (is.numeric(j) && any(colnames(x)[j] == time_column)))) || 258 | !missing(i) && !drop && ((is.character(i)) && any(i == time_column) || is.numeric(i) || is.logical(i))) { 259 | structure(NextMethod(), class = class(x), time_column = time_column) 260 | } else { 261 | x <- structure(x, class = setdiff(class(x), "sftime"), time_column = NULL) 262 | NextMethod() 263 | } # ---todo: what to do when i is an sftime object: match also time info 264 | 265 | } 266 | 267 | #' @name st_sftime 268 | #' @param value An object to insert into \code{x} or with which to rename 269 | #' columns of \code{x}. 270 | #' @examples 271 | #' ## Assigning values to columns 272 | #' 273 | #' # assigning new values to a non-time column 274 | #' x[["a"]] <- 5:1 275 | #' class(x) 276 | #' 277 | #' # assigning allowed new values to the time column 278 | #' x[["time"]] <- Sys.time() + 1:5 279 | #' class(x) 280 | #' 281 | #' # assigning new values to the time column which invalidate the time column 282 | #' x[["time"]] <- list(letters[1:2]) 283 | #' class(x) 284 | #' 285 | #' @export 286 | "[[<-.sftime" <- function(x, i, value) { 287 | time_column_name <- attr(x, "time_column") 288 | reclass_sftime(NextMethod(), time_column_name = time_column_name) 289 | } 290 | 291 | #' @name st_sftime 292 | #' @examples 293 | #' # assigning new values with `$` 294 | #' x$time <- Sys.time() + 1:5 295 | #' class(x) 296 | #' 297 | #' @export 298 | "$<-.sftime" = function(x, i, value) { 299 | structure(NextMethod(), class = c("sftime", setdiff(class(x), "sftime"))) 300 | } 301 | 302 | 303 | ##' name st_sftime 304 | ##' examples 305 | ##' # renaming column names 306 | ##' names(x)[1] <- "b" 307 | ##' 308 | ##' export 309 | #"names<-.sftime" <- function(x, value) { 310 | # out <- NextMethod() 311 | # dplyr_reconstruct.sftime(out, x) 312 | #} # ---todo: raises an error 313 | 314 | #### printing #### 315 | 316 | #' Helper function to print time columns when printing an \code{sftime} object 317 | #' 318 | #' @noRd 319 | #' @keywords internal 320 | #' @param x A time column from a \code{\link[=st_sftime]{sftime}} object. 321 | #' @param n An integer value; The first \code{n} elements of \code{x} to print. 322 | #' @param print_number_features A logical value; whether the number of features 323 | #' shall be printed (\code{TRUE}) or not (\code{FALSE}). 324 | #' 325 | #' @return \code{x} (invisible). 326 | print_time_column <- function(x, n = 5L, print_number_features = FALSE) { 327 | 328 | stopifnot(is.logical(print_number_features) && length(print_number_features) == 1) 329 | stopifnot(is.integer(n) && length(n) == 1) 330 | 331 | ord <- order(x, na.last = NA) 332 | if(length(x) != 0) { 333 | x_min <- x[[ord[[1]]]] 334 | x_max <- x[[ord[[length(ord)]]]] 335 | } else { 336 | x_min <- x_max <- NA 337 | } 338 | 339 | x_class <- class(x) 340 | x_is_value <- length(x) == 1 341 | 342 | cat(paste0("Time column with ", 343 | ifelse(!print_number_features, "", 344 | paste0(length(x), ifelse(x_is_value, " feature of ", " features, each of "))), 345 | ifelse(length(x_class) == 1, "class", "classes"), ": \'", 346 | paste0(x_class, collapse="\', \'"), "\'.\n", 347 | ifelse(x_is_value, 348 | paste0("Representing ", x_min, ".\n" ), 349 | paste0("Ranging from ", x_min, " to ", x_max, ".\n" )))) 350 | 351 | for(i in seq_len(min(n, length(x)))) { 352 | ret <- x[[i]] 353 | class(ret) <- setdiff(class(ret), "tc") 354 | message(ret) 355 | } 356 | 357 | invisible(x) 358 | } 359 | 360 | #' Prints an \code{sftime} object 361 | #' 362 | #' @param x An object of class \code{sftime}. 363 | #' @param ... Currently unused arguments, for compatibility. 364 | #' @param n Numeric value; maximum number of printed elements. 365 | #' 366 | #' @return \code{x} (invisible). 367 | #' @examples 368 | #' g <- st_sfc(st_point(c(1, 2)), st_point(c(1, 3)), st_point(c(2, 3)), 369 | #' st_point(c(2, 1)), st_point(c(3, 1))) 370 | #' tc <- Sys.time() + 1:5 371 | #' x <- st_sftime(a = 1:5, g, time = tc) 372 | #' print(x) 373 | #' print(x[0, ]) 374 | #' 375 | #' @export 376 | print.sftime <- function(x, ..., n = getOption("sf_max_print", default = 10)) { 377 | geoms <- which(vapply(x, function(col) inherits(col, "sfc"), TRUE)) 378 | nf <- length(x) - length(geoms) - 1 379 | app <- paste("and", nf, ifelse(nf == 1, "field", "fields")) 380 | if (any(!is.na(st_agr(x)))) { 381 | su = summary(st_agr(x)) 382 | summ = paste(paste(su, names(su)), collapse = ", ") 383 | app <- paste0(app, "\n", "Attribute-geometry relationship: ", summ) 384 | } 385 | if (length(geoms) > 1) 386 | app <- paste0(app, "\n", "Active geometry column: ", attr(x, "sf_column")) 387 | print(st_geometry(x), n = 0, what = "Spatiotemporal feature collection with", append = app) 388 | 389 | # temporal information 390 | print_time_column(x[, attr(x, "time_column"), drop = TRUE], n = 0L, print_number_features = FALSE) 391 | 392 | if(n > 0) { 393 | if (inherits(x, "tbl_df")) { 394 | x_print <- x 395 | class(x_print) <- setdiff(class(x_print), c("sftime", "sf")) 396 | print(x_print) 397 | } else { 398 | y <- x 399 | if(nrow(y) > n) { 400 | cat(paste("First", n, "features:\n")) 401 | y <- x[seq_len(n), , drop = FALSE] 402 | } 403 | print.data.frame(y, ...) 404 | } 405 | } 406 | invisible(x) 407 | } 408 | 409 | 410 | #### coercion #### 411 | 412 | #' Convert a foreign object to an \code{sftime} object 413 | #' 414 | #' @name st_as_sftime 415 | #' @param x An object to be converted into an object of class 416 | #' \code{\link[=st_sftime]{sftime}}. 417 | #' @param ... Further arguments passed to methods. 418 | #' 419 | #' @return \code{x} converted to an \code{sftime} object. 420 | #' 421 | #' @export 422 | #' @importFrom methods slotNames as 423 | st_as_sftime = function(x, ...) UseMethod("st_as_sftime") 424 | 425 | #' @name st_as_sftime 426 | #' @examples 427 | #' # modified from spacetime: 428 | #' library(sp) 429 | #' library(spacetime) 430 | #' 431 | #' sp <- cbind(x = c(0,0,1), y = c(0,1,1)) 432 | #' row.names(sp) <- paste("point", 1:nrow(sp), sep="") 433 | #' sp <- SpatialPoints(sp) 434 | #' time <- as.POSIXct("2010-08-05") + 3600 * (10:12) 435 | #' x <- STI(sp, time) 436 | #' 437 | #' st_as_sftime(x) 438 | #' 439 | #' @export 440 | st_as_sftime.ST <- function(x, ...) { 441 | has_data <- "data" %in% slotNames(x) 442 | 443 | if (!inherits(x, "STI")) { 444 | if (has_data) 445 | x <- as(x, "STIDF") 446 | else 447 | x <- as(x, "STI") 448 | } 449 | 450 | times <- as.POSIXct(attr(x@time, "index"), origin = "1970-01-01") 451 | 452 | if (has_data) 453 | st_sftime(x@data, st_as_sfc(x@sp), time = times) 454 | else 455 | st_sftime(st_as_sfc(x@sp), time = times) 456 | } 457 | 458 | #' @name st_as_sftime 459 | #' @examples 460 | #' # convert a Track object from package trajectories to an sftime object 461 | #' library(trajectories) 462 | #' x1_Track <- trajectories::rTrack(n = 100) 463 | #' x1_Track@data$speed <- sort(rnorm(length(x1_Track))) 464 | #' x1_sftime <- st_as_sftime(x1_Track) 465 | #' 466 | #' @export 467 | st_as_sftime.Track <- function(x, ...) { 468 | 469 | has_data <- "data" %in% slotNames(x) 470 | 471 | if (has_data) 472 | x <- as(x, "STIDF") 473 | else 474 | x <- as(x, "STI") 475 | 476 | st_as_sftime(x) 477 | 478 | } 479 | 480 | #' @name st_as_sftime 481 | #' @return \code{st_as_sftime.Tracks} furthermore adds a column 482 | #' \code{track_name} with the names of the \code{tracks} slot of the input 483 | #' \code{Tracks} object. 484 | #' 485 | #' @examples 486 | #' # convert a Tracks object from package trajectories to an sftime object 487 | #' x2_Tracks <- trajectories::rTracks(m = 6) 488 | #' x2_sftime <- st_as_sftime(x2_Tracks) 489 | #' 490 | #' @export 491 | st_as_sftime.Tracks <- function(x, ...) { 492 | 493 | track_name <- 494 | unlist(lapply(seq_along(x@tracks), function(i) rep(names(x@tracks)[[i]], x@tracksData$n[[i]]))) 495 | 496 | cbind(st_as_sftime(as(x, "STIDF")), track_name = track_name) 497 | 498 | } 499 | 500 | #' @name st_as_sftime 501 | #' @return \code{st_as_sftime.TracksCollection} furthermore adds the columns 502 | #' \code{tracks_name} with the names of the \code{tracksCollection} slot and 503 | #' \code{track_name} with the names of the \code{tracks} slot of the input 504 | #' \code{Tracks} object. 505 | #' 506 | #' @examples 507 | #' # convert a TracksCollection object from package trajectories to an sftime object 508 | #' x3_TracksCollection <- trajectories::rTracksCollection(p = 2, m = 3, n = 50) 509 | #' x3_sftime <- st_as_sftime(x3_TracksCollection) 510 | #' 511 | #' @export 512 | st_as_sftime.TracksCollection <- function(x, ...) { 513 | 514 | track_names <- 515 | do.call(rbind, lapply(seq_along(x@tracksCollection), function(i) { 516 | n <- sum(x@tracksCollection[[i]]@tracksData$n) 517 | track_i <- x@tracksCollection[[i]] 518 | data.frame( 519 | tracks_name = rep(names(x@tracksCollection)[[i]], n), 520 | track_name = unlist(lapply(seq_along(track_i@tracks), function(j) rep(names(track_i@tracks)[[j]], track_i@tracksData$n[[j]]))), 521 | stringsAsFactors = FALSE 522 | ) 523 | })) 524 | 525 | cbind(st_as_sftime(as(x, "STIDF")), track_names) 526 | 527 | } 528 | 529 | #' @name st_as_sftime 530 | #' @examples 531 | #' # convert an sftime object to an sftime object 532 | #' st_as_sftime(x3_sftime) 533 | #' 534 | #' @export 535 | st_as_sftime.sftime <- function(x, ...) x 536 | 537 | #' @name st_as_sftime 538 | #' @param time_column_name A character value; name of the active time column. In 539 | #' case there is more than one and \code{time_column_name} is \code{NULL}, the 540 | #' first one is taken. 541 | #' @examples 542 | #' # convert an sf object to an sftime object 543 | #' g <- st_sfc(st_point(c(1, 2)), st_point(c(1, 3)), st_point(c(2, 3)), 544 | #' st_point(c(2, 1)), st_point(c(3, 1))) 545 | #' x4_sf <- st_sf(a = 1:5, g, time = Sys.time() + 1:5) 546 | #' x4_sftime <- st_as_sftime(x4_sf) 547 | #' 548 | #' @export 549 | st_as_sftime.sf <- function(x, ..., time_column_name = NULL) { 550 | st_sftime(x, ..., time_column_name = time_column_name) 551 | } 552 | 553 | #' @name st_as_sftime 554 | #' @param long A logical value; See \code{\link[stars:st_as_sf]{st_as_sf}}. 555 | #' Typically, \code{long} should be set to \code{TRUE} since time information 556 | #' typically is a dimension of a \code{stars} object. 557 | #' @examples 558 | #' # convert a Tracks object from package trajectories to an sftime object 559 | #' x5_stars <- stars::read_stars(system.file("nc/bcsd_obs_1999.nc", package = "stars")) 560 | #' x5_sftime <- st_as_sftime(x5_stars, time_column_name = "time") 561 | #' 562 | #' # this requires some thought to not accidentally drop time dimensions. For 563 | #' # example, setting `merge = TRUE` will drop the time dimension and thus throw 564 | #' # an error: 565 | #' \dontrun{ 566 | #' x5_sftime <- st_as_sftime(x5_stars, merge = TRUE, time_column_name = "time") 567 | #' } 568 | #' 569 | #' @export 570 | st_as_sftime.stars <- function(x, ..., long = TRUE, time_column_name = NULL) { 571 | res <- sf::st_as_sf(x, ..., long = long) 572 | if(!time_column_name %in% colnames(res)) 573 | stop("`time_column_name` is not a column in the converted object.") 574 | st_sftime(res, time_column_name = time_column_name) 575 | } 576 | 577 | #' @name st_as_sftime 578 | #' @param agr A character vector; see the details section of \code{\link[sf]{st_sf}}. 579 | #' @param coords In case of point data: names or numbers of the numeric columns 580 | #' holding coordinates. 581 | #' @param wkt The name or number of the character column that holds WKT encoded 582 | #' geometries. 583 | #' @param dim Passed on to \code{\link[sf]{st_point}} (only when argument 584 | #' \code{coords} is given). 585 | #' @param remove A logical value; when \code{coords} or \code{wkt} is given, 586 | #' remove these columns from \code{x}? 587 | #' @param na.fail A logical value; if \code{TRUE}, raise an error if coordinates 588 | #' contain missing values. 589 | #' @inheritParams st_sftime 590 | #' @examples 591 | #' # convert a data frame to an sftime object 592 | #' x5_df <- 593 | #' data.frame(a = 1:5, g, time = Sys.time() + 1:5, stringsAsFactors = FALSE) 594 | #' x5_sftime <- st_as_sftime(x5_df) 595 | #' 596 | #' @export 597 | st_as_sftime.data.frame <- 598 | function(x, 599 | ..., 600 | agr = NA_agr_, 601 | coords, 602 | wkt, 603 | dim = "XYZ", 604 | remove = TRUE, 605 | na.fail = TRUE, 606 | sf_column_name = NULL, 607 | time_column_name = NULL, 608 | time_column_last = FALSE) { 609 | 610 | st_sftime( 611 | sf::st_as_sf( 612 | x, 613 | ..., 614 | agr = agr, 615 | coords = coords, 616 | wkt = wkt, 617 | dim = dim, 618 | remove = remove, 619 | na.fail = na.fail, 620 | sf_column_name = sf_column_name 621 | ), 622 | time_column_name = time_column_name, 623 | time_column_last = time_column_last 624 | ) 625 | } 626 | 627 | #' @name st_as_sftime 628 | #' @examples 629 | #' # convert a ppp object to an sftime object (modified from the sf package) 630 | #' if (require(spatstat.geom)) { 631 | #' st_as_sftime(gorillas, time_column_name = "date") 632 | #' } 633 | #' 634 | #' @export 635 | st_as_sftime.ppp <- function(x, ..., time_column_name) { 636 | st_sftime(sf::st_as_sf(x), time_column_name = time_column_name) 637 | } 638 | 639 | #' @name st_as_sftime 640 | #' @examples 641 | #' # convert a psp object to an sftime object (modified from the spatstat.geom 642 | #' # package) 643 | #' if (require(spatstat.geom)) { 644 | #' # modified from spatstat.geom: 645 | #' x_psp <- 646 | #' psp( 647 | #' runif(10), runif(10), runif(10), runif(10), window=owin(), 648 | #' marks = data.frame(time = Sys.time() + 1:10) 649 | #' ) 650 | #' st_as_sftime(x_psp, time_column_name = "time") 651 | #' } 652 | #' 653 | #' @export 654 | st_as_sftime.psp <- function(x, ..., time_column_name) { 655 | st_sftime(sf::st_as_sf(x), time_column_name = time_column_name) 656 | } 657 | 658 | #' @name st_as_sftime 659 | #' @examples 660 | #' # convert an lpp object to an sftime object (modified from the 661 | #' # spatstat.linnet package) 662 | #' if (require(spatstat.geom) && require(spatstat.linnet)) { 663 | #' # modified from spatstat.linnet: 664 | #' 665 | #' # letter 'A' 666 | #' v <- spatstat.geom::ppp(x=(-2):2, y=3*c(0,1,2,1,0), c(-3,3), c(-1,7)) 667 | #' edg <- cbind(1:4, 2:5) 668 | #' edg <- rbind(edg, c(2,4)) 669 | #' letterA <- spatstat.linnet::linnet(v, edges=edg) 670 | #' 671 | #' # points on letter A 672 | #' xx <- 673 | #' spatstat.geom::ppp( 674 | #' x=c(-1.5,0,0.5,1.5), y=c(1.5,3,4.5,1.5), 675 | #' marks = data.frame(time = Sys.time() + 1:4, a = 1:4), 676 | #' window = spatstat.geom::owin( 677 | #' xrange = range(c(-1.5,0,0.5,1.5)), 678 | #' yrange = range(c(1.5,3,4.5,1.5))) 679 | #' ) 680 | #' x_lpp <- spatstat.linnet::lpp(xx, letterA) 681 | #' 682 | #' # convert to sftime 683 | #' st_as_sftime(x_lpp, time_column_name = "time") 684 | #' } 685 | #' 686 | #' @export 687 | st_as_sftime.lpp <- function(x, ..., time_column_name) { 688 | st_sftime(sf::st_as_sf(x), time_column_name = time_column_name) 689 | } 690 | 691 | #' @name st_as_sftime 692 | #' @examples 693 | #' # convert an sftrack object to an sftime object (modified from sftrack) 694 | #' if (require(sftrack)) { 695 | #' 696 | #' # get an sftrack object 697 | #' data("raccoon") 698 | #' 699 | #' raccoon$timestamp <- as.POSIXct(raccoon$timestamp, "EST") 700 | #' 701 | #' burstz <- 702 | #' list(id = raccoon$animal_id, month = as.POSIXlt(raccoon$timestamp)$mon) 703 | #' 704 | #' x_sftrack <- 705 | #' as_sftrack(raccoon, 706 | #' group = burstz, time = "timestamp", 707 | #' error = NA, coords = c("longitude", "latitude") 708 | #' ) 709 | #' 710 | #' # convert to sftime 711 | #' st_as_sftime(x_sftrack) 712 | #' } 713 | #' 714 | #' @export 715 | st_as_sftime.sftrack <- function(x, ...) { 716 | time_column_name <- attr(x, which = "time_column") 717 | attr(x, which = "group_col") <- NULL 718 | attr(x, which = "error_col") <- NULL 719 | class(x) <- setdiff(class(x), "sftrack") 720 | st_sftime(x, time_column_name = time_column_name) 721 | } 722 | 723 | #' @name st_as_sftime 724 | #' @examples 725 | #' # convert an sftraj object to an sftime object (modified from sftrack) 726 | #' if (require(sftrack)) { 727 | #' 728 | #' # get an sftrack object 729 | #' data("raccoon") 730 | #' 731 | #' raccoon$timestamp <- as.POSIXct(raccoon$timestamp, "EST") 732 | #' 733 | #' burstz <- 734 | #' list(id = raccoon$animal_id, month = as.POSIXlt(raccoon$timestamp)$mon) 735 | #' 736 | #' x_sftraj <- 737 | #' as_sftraj(raccoon, 738 | #' time = "timestamp", 739 | #' error = NA, coords = c("longitude", "latitude"), 740 | #' group = burstz 741 | #' ) 742 | #' 743 | #' # convert to sftime 744 | #' st_as_sftime(x_sftraj) 745 | #' } 746 | #' 747 | #' @export 748 | st_as_sftime.sftraj <- function(x, ...) { 749 | time_column_name <- attr(x, which = "time_column") 750 | attr(x, which = "group_col") <- NULL 751 | attr(x, which = "error_col") <- NULL 752 | class(x) <- setdiff(class(x), "sftraj") 753 | st_sftime(x, time_column_name = time_column_name) 754 | } 755 | 756 | #' @name st_as_sftime 757 | #' @inheritParams cubble::make_spatial_sf 758 | #' @examples 759 | #' # convert a cubble_df object from package cubble to an sftime object 760 | #' if (requireNamespace("cubble", quietly = TRUE, versionCheck = "0.3.0")) { 761 | #' 762 | #' # get a cubble_df object 763 | #' data("climate_aus", package = "cubble") 764 | #' 765 | #' # convert to sftime 766 | #' climate_aus_sftime <- 767 | #' st_as_sftime(climate_aus[1:4, ]) 768 | #' 769 | #' climate_aus_sftime <- 770 | #' st_as_sftime(cubble::face_temporal(climate_aus)[1:4, ]) 771 | #' 772 | #' } 773 | #' @export 774 | st_as_sftime.cubble_df <- function(x, ..., sfc = NULL, crs, silent = FALSE) { 775 | 776 | if (! requireNamespace("cubble", quietly = TRUE, versionCheck = "0.3.0")) 777 | stop("You need the `cubble` package (>= 0.3.0) to use this function. Install that first.") 778 | 779 | # make sure the cubble_df object has the right format 780 | if(! cubble::is_cubble_spatial(x)) { 781 | x <- cubble::face_spatial(data = x) 782 | } 783 | if(! inherits(x, "sf")) { 784 | x <- cubble::make_spatial_sf(x, sfc = sfc, crs = crs, silent = silent) 785 | } 786 | 787 | # extract information needed to create the sftime object 788 | time_column_name <- attr(x, which = "index") 789 | id_column_name <- utils::head(names(attr(x, "key")), -1) 790 | column_names <- c(setdiff(colnames(x), "ts"), colnames(x$ts[[1]])) 791 | x_ts <- as.data.frame(cubble::face_temporal(x, col = "ts")) 792 | 793 | # convert to sf (drop all cubble_df attributes) 794 | attr(x, which = "form") <- NULL 795 | attr(x, which = "coords") <- NULL 796 | attr(x, which = "index") <- NULL 797 | class(x) <- setdiff(class(x), c("cubble_df", "spatial_cubble_df")) 798 | 799 | # merge spatial and temporal faces 800 | x <- merge(x_ts, x[, !colnames(x) == "ts"], by = id_column_name) 801 | x <- x[, column_names] 802 | 803 | st_as_sftime(x, time_column_name = time_column_name) 804 | 805 | } 806 | 807 | 808 | 809 | #### transform attributes #### 810 | 811 | #' Transform method for \code{sftime} objects 812 | #' 813 | #' Can be used to create or modify attribute variables; for transforming 814 | #' geometries see \code{\link[sf]{st_transform}}, and all other functions starting with 815 | #' \code{st_}. 816 | #' 817 | #' @param _data An object of class \code{\link[=st_sftime]{sftime}}. 818 | #' @inheritParams sf::transform.sf 819 | #' 820 | #' @return \code{_data} (an \code{sftime} object) with modified attribute values 821 | #' (columns). 822 | #' 823 | #' @examples 824 | #' # create an sftime object 825 | #' g <- st_sfc(st_point(c(1, 2)), st_point(c(1, 3)), st_point(c(2, 3)), 826 | #' st_point(c(2, 1)), st_point(c(3, 1))) 827 | #' x <- 828 | #' data.frame(a = 1:5, g, time = Sys.time() + 1:5, stringsAsFactors = FALSE) 829 | #' x_sftime <- st_as_sftime(x) 830 | #' x_sftime 831 | #' 832 | #' # modify values in column a 833 | #' transform(x_sftime, a = rev(a)) 834 | #' 835 | #' @export 836 | transform.sftime <- function (`_data`, ...) { 837 | reclass_sftime(NextMethod(), time_column_name = attr(`_data`, "time_column")) 838 | } 839 | -------------------------------------------------------------------------------- /R/st_cast.R: -------------------------------------------------------------------------------- 1 | #' Cast geometry to another type: either simplify, or cast explicitly 2 | #' 3 | #' @name st_cast 4 | #' @inheritParams sf::st_cast 5 | #' @param x An object of class \code{sftime}. 6 | #' @return \code{x} with changed geometry type. 7 | #' @examples 8 | #' # cast from POINT to LINESTRING 9 | #' g <- st_sfc(st_point(1:2), st_point(c(2, 4))) 10 | #' time <- Sys.time() 11 | #' x <- 12 | #' st_sftime(a = 3:4, g, time = time) %>% 13 | #' dplyr::group_by(time) %>% 14 | #' dplyr::summarize(do_union = TRUE) %>% 15 | #' st_cast(to = "LINESTRING") 16 | #' @export 17 | st_cast.sftime <- function(x, to, ..., warn = TRUE, do_split = TRUE) { 18 | reclass_sftime(NextMethod(), attr(x, "time_column")) 19 | } -------------------------------------------------------------------------------- /R/st_geometry.R: -------------------------------------------------------------------------------- 1 | #' Drops the geometry column of \code{sftime} objects 2 | #' 3 | #' Drops the geometry column of an \code{sftime} object. This will also drop 4 | #' the \code{sftime} class attribute and \code{time_column} attribute. 5 | #' 6 | #' @name st_geometry 7 | #' @inheritParams sf::st_drop_geometry 8 | #' @param x An \code{sftime} object. 9 | #' @return \code{x} without geometry column and without \code{sftime} and 10 | #' \code{sf} class. 11 | #' @examples 12 | #' # dropping the geometry column will also drop the `sftime` class: 13 | #' g <- st_sfc(st_point(1:2)) 14 | #' time <- Sys.time() 15 | #' x <- st_sftime(a = 3, g, time = time) 16 | #' st_drop_geometry(x) 17 | #' 18 | #' @export 19 | st_drop_geometry.sftime <- function(x, ...) { 20 | class(x) <- setdiff(class(x), "sftime") 21 | NextMethod() 22 | } -------------------------------------------------------------------------------- /R/st_time.R: -------------------------------------------------------------------------------- 1 | #' Get, set, or replace time information 2 | #' 3 | #' @param obj An object of class \code{sftime}. 4 | #' @param x An object of class \code{sftime} or \code{sf}. 5 | #' @param ... Additional arguments; Ignored. 6 | #' @param time_column_name Character value; The name of the column to set as 7 | #' active time column in \code{x}. 8 | #' @param value An object for which \code{\link{is_sortable}} returns 9 | #' \code{TRUE} or an object of class \code{character}, or \code{NULL}. 10 | #' 11 | #' @details In case \code{value} is character and \code{x} is of class 12 | #' \code{sftime}, the active time column (as indicated by attribute 13 | #' \code{time_column}) is set to \code{x[[value]]}. 14 | #' 15 | #' The replacement function applied to \code{sftime} objects will overwrite the 16 | #' active time column, if \code{value} is \code{NULL}, it will remove it and 17 | #' coerce \code{x} to an \code{sftime} object. 18 | #' 19 | #' @return \code{st_time} returns the content of the active time column of an 20 | #' \code{sftime} object. 21 | #' Assigning an object for which \code{\link{is_sortable}} returns \code{TRUE} 22 | #' to an \code{sf} object creates an \code{\link[=st_sftime]{sftime}} object. 23 | #' Assigning an object for which \code{\link{is_sortable}} returns \code{TRUE} 24 | #' to an \code{sftime} object replaces the active time column by this object. 25 | #' @export 26 | st_time <- function(obj, ...) UseMethod("st_time") 27 | 28 | #' @rdname st_time 29 | #' @export 30 | `st_time<-` = function(x, ..., value) UseMethod("st_time<-") 31 | 32 | #' @rdname st_time 33 | #' @export 34 | #' @examples 35 | #' # from sftime object 36 | #' g <- st_sfc(st_point(1:2)) 37 | #' time <- Sys.time() 38 | #' x <- st_sftime(a = 3, g, time = time) 39 | #' st_time(x) 40 | #' 41 | st_time.sftime <- function(obj, ...) { 42 | ret <- obj[[attr(obj, "time_column")]] 43 | if (!is_sortable(ret)) # corrupt! 44 | stop('attr(obj, "time_column") does not point to a time column.\nDid you rename it, without setting st_time(obj) <- "newname"?') 45 | ret 46 | } 47 | 48 | #' @rdname st_time 49 | #' @export 50 | #' @examples 51 | #' ## assign a vector with time information 52 | #' 53 | #' # to sf object 54 | #' x <- st_sf(a = 3, g) 55 | #' st_time(x) <- time 56 | #' x 57 | #' 58 | `st_time<-.sf` <- function(x, ..., time_column_name = "time", value) { 59 | stopifnot(is_sortable(value)) 60 | stopifnot(is.character(time_column_name) && length(time_column_name) == 1) 61 | 62 | x[[time_column_name]] <- value 63 | st_sftime(x, time_column_name = time_column_name) 64 | } 65 | 66 | #' @rdname st_time 67 | #' @export 68 | #' @examples 69 | #' # to sftime object 70 | #' x <- st_sftime(a = 3, g, time = time) 71 | #' st_time(x) <- Sys.time() 72 | #' 73 | #' ## change the time column to another already existing column 74 | #' st_time(x) <- "a" 75 | #' 76 | #' ## remove time column from sftime object 77 | #' st_time(x) <- NULL 78 | #' 79 | `st_time<-.sftime` = function(x, ..., value) { 80 | 81 | if (! is.null(value)) { 82 | stopifnot(is_sortable(value) || is.character(value)) 83 | } 84 | 85 | if (! is.null(value) && is.character(value) && length(value) == 1 && value %in% colnames(x)) {# set flag to another column 86 | attr(x, "time_column") <- value 87 | } else {# replace, remove, or set list-column 88 | x[[attr(x, "time_column")]] <- value 89 | } 90 | 91 | if (is.null(value)) 92 | structure(x, time_column = NULL, class = setdiff(class(x), "sftime")) 93 | else 94 | st_as_sftime(x) 95 | } 96 | 97 | #' @rdname st_time 98 | #' @export 99 | #' @examples 100 | #' ## pipe-friendly 101 | #' 102 | #' # assign time column to sf object 103 | #' x <- st_sf(a = 3, g) 104 | #' x <- st_set_time(x, time) 105 | #' 106 | #' # remove time column from sftime object 107 | #' st_set_time(x, NULL) 108 | #' 109 | st_set_time <- function(x, value, ...) { 110 | st_time(x, ...) <- value 111 | x 112 | } 113 | 114 | #' @rdname st_time 115 | #' @export 116 | #' @details \code{st_drop_time} drops the time column of its argument, and 117 | #' reclasses it accordingly. 118 | #' @examples 119 | #' ## drop time column and class 120 | #' 121 | #' # same as x <- st_set_time(x, NULL) 122 | #' st_drop_time(x) 123 | #' 124 | st_drop_time = function(x) { 125 | if (!inherits(x, "sftime")) 126 | stop("`st_drop_time` only works with objects of class sftime") 127 | st_set_time(x, NULL) 128 | } -------------------------------------------------------------------------------- /R/tidyverse.R: -------------------------------------------------------------------------------- 1 | # Tidyverse methods (See also join.R) 2 | 3 | #' 'tidyverse' methods for \code{sftime} objects 4 | #' 5 | #' 'tidyverse' methods for \code{sftime} objects. Geometries are sticky, use 6 | #' \code{\link{as.data.frame}} to let \code{dplyr}'s own methods drop them. Use 7 | #' these methods without the \code{.sftime} suffix and after loading the 8 | #' 'tidyverse' package with the generic (or after loading package 'tidyverse'). 9 | #' @name tidyverse 10 | #' @inheritParams sf::tidyverse 11 | #' @inheritParams tidyr::pivot_longer 12 | #' @param x An object of class \code{sftime}. 13 | #' @param y See \code{dplyr::`mutate-joins`}. 14 | #' @param .data An object of class \code{stime}. 15 | #' @return 16 | #' \itemize{ 17 | #' \item For \code{_join} methods: An object of class \code{sftime} 18 | #' representing the joining result of \code{x} and \code{y}. See 19 | #' \code{\link[dplyr]{mutate-joins}}. 20 | #' \item For \code{filter}: See \code{\link[dplyr]{filter}}. 21 | #' \item For \code{arrange}: See \code{\link[dplyr]{arrange}}. 22 | #' \item For \code{group_by} and \code{ungroup}: A grouped \code{sftime} 23 | #' object. See \code{\link[dplyr]{arrange}}. 24 | #' \item For \code{rowwise}: An \code{sftime} object. See 25 | #' \code{\link[dplyr]{rowwise}}. 26 | #' \item For \code{mutate} and \code{transmute}: See 27 | #' \code{\link[dplyr]{mutate}}. 28 | #' \item For \code{select}: See \code{\link[dplyr]{select}}. If the active 29 | #' time column is not explicitly selected, a \code{sf} object is returned. 30 | #' \item For \code{rename}: See \code{\link[dplyr]{rename}}. 31 | #' \item For \code{slice}: See \code{\link[dplyr]{slice}}. 32 | #' \item For \code{summarize} and \code{summarise}: See 33 | #' \code{\link[dplyr]{summarise}}. 34 | #' \item For \code{distinct}: See \code{\link[dplyr]{distinct}}. 35 | #' \item For \code{gather}: See \code{\link[tidyr]{gather}}. 36 | #' } 37 | #' 38 | NULL 39 | 40 | #' @rdname tidyverse 41 | #' @examples 42 | #' ## filter 43 | #' filter(x1, a <= 2) 44 | #' 45 | filter.sftime <- function(.data, ..., .dots) { 46 | reclass_sftime(NextMethod(), time_column_name = attr(.data, "time_column")) 47 | } 48 | 49 | #' @rdname tidyverse 50 | #' @examples 51 | #' ## arrange 52 | #' arrange(x1, dplyr::desc(a)) 53 | #' 54 | arrange.sftime <- function(.data, ..., .dots) { 55 | reclass_sftime(NextMethod(), time_column_name = attr(.data, "time_column")) 56 | } 57 | 58 | #' @rdname tidyverse 59 | #' @examples 60 | #' ## group_by 61 | #' group_by(x1, time) 62 | #' 63 | group_by.sftime <- function(.data, ..., add = FALSE) { 64 | reclass_sftime(NextMethod(), time_column_name = attr(.data, "time_column")) 65 | } 66 | 67 | #' @rdname tidyverse 68 | #' @examples 69 | #' ## ungroup 70 | #' ungroup(group_by(x1, time)) 71 | #' 72 | ungroup.sftime <- function(.data, ...) { 73 | reclass_sftime(NextMethod(), time_column_name = attr(.data, "time_column")) 74 | } 75 | 76 | #' @rdname tidyverse 77 | #' @examples 78 | #' ## rowwise 79 | #' x1 %>% 80 | #' mutate(a1 = 5:7) %>% 81 | #' rowwise() %>% 82 | #' mutate(a2 = mean(a, a1)) 83 | #' 84 | rowwise.sftime <- function(.data, ...) { 85 | reclass_sftime(NextMethod(), time_column_name = attr(.data, "time_column")) 86 | } 87 | 88 | #' @rdname tidyverse 89 | #' @examples 90 | #' ## mutate 91 | #' x1 %>% 92 | #' mutate(a1 = 5:7) 93 | #' 94 | mutate.sftime <- function(.data, ..., .dots) { 95 | reclass_sftime(NextMethod(), time_column_name = attr(.data, "time_column")) 96 | } 97 | 98 | #' @rdname tidyverse 99 | #' @examples 100 | #' ## transmute 101 | #' x1 %>% 102 | #' transmute(a1 = 5:7) 103 | #' 104 | transmute.sftime <- function(.data, ..., .dots) { 105 | reclass_sftime(NextMethod(), time_column_name = attr(.data, "time_column")) 106 | } 107 | 108 | #' @rdname tidyverse 109 | #' @examples 110 | #' ## select 111 | #' x1 %>% 112 | #' select(-time) %>% 113 | #' select(geometry) 114 | #' 115 | select.sftime <- function(.data, ...) { 116 | reclass_sftime(NextMethod(), time_column_name = attr(.data, "time_column")) 117 | } 118 | 119 | #' @rdname tidyverse 120 | #' @examples 121 | #' ## rename 122 | #' x1 %>% 123 | #' rename(a1 = a) 124 | #' 125 | rename.sftime <- function(.data, ...) { 126 | reclass_sftime(NextMethod(), time_column_name = attr(.data, "time_column")) 127 | } 128 | 129 | #' @rdname tidyverse 130 | #' @examples 131 | #' ## slice 132 | #' x1 %>% 133 | #' slice(1:2) 134 | #' 135 | slice.sftime <- function(.data, ..., .dots) { 136 | reclass_sftime(NextMethod(), time_column_name = attr(.data, "time_column")) 137 | } 138 | 139 | #' @rdname tidyverse 140 | #' @examples 141 | #' ## summarise 142 | #' x1 %>% 143 | #' summarise(time = mean(time)) 144 | #' 145 | #' x1 %>% 146 | #' summarize(time = mean(time)) 147 | #' 148 | summarise.sftime <- function(.data, ..., .dots, do_union = TRUE, is_coverage = FALSE) { 149 | reclass_sftime(NextMethod(), time_column_name = attr(.data, "time_column")) 150 | } 151 | 152 | #' @rdname tidyverse 153 | summarize.sftime <- summarise.sftime 154 | 155 | #' @rdname tidyverse 156 | #' @examples 157 | #' ## distinct 158 | #' x1 %>% 159 | #' distinct(geometry) 160 | #' 161 | distinct.sftime <- function(.data, ..., .keep_all = FALSE) { 162 | reclass_sftime(NextMethod(), time_column_name = attr(.data, "time_column")) 163 | } 164 | 165 | #' @rdname tidyverse 166 | #' @examples 167 | #' ## gather 168 | #' library(tidyr) 169 | #' x1 %>% 170 | #' mutate(a1 = 5:7) %>% 171 | #' gather(key = "variable", value = "value", a, a1) 172 | #' 173 | gather.sftime <- function(data, key, value, ..., na.rm = FALSE, convert = FALSE, factor_key = FALSE) { 174 | reclass_sftime(NextMethod(), time_column_name = attr(data, "time_column")) 175 | } 176 | 177 | #' @rdname tidyverse 178 | #' @examples 179 | #' ## pivot_longer 180 | #' x1 %>% 181 | #' mutate(a1 = 5:7) %>% 182 | #' pivot_longer(cols = c("a", "a1"), names_to = "variable", values_to = "value") 183 | #' 184 | pivot_longer.sftime <- function (data, cols, names_to = "name", names_prefix = NULL, 185 | names_sep = NULL, names_pattern = NULL, names_ptypes = NULL, 186 | names_transform = NULL, names_repair = "check_unique", 187 | values_to = "value", values_drop_na = FALSE, values_ptypes = NULL, 188 | values_transform = NULL, ...) { 189 | reclass_sftime(NextMethod(), time_column_name = attr(data, "time_column")) 190 | } 191 | 192 | #' @rdname tidyverse 193 | #' @examples 194 | #' ## spread 195 | #' x1 %>% 196 | #' mutate(a1 = 5:7) %>% 197 | #' gather(key = "variable", value = "value", a, a1) %>% 198 | #' spread(key = "variable", value = "value") 199 | #' 200 | spread.sftime <- function(data, key, value, fill = NA, convert = FALSE, drop = TRUE, 201 | sep = NULL) { 202 | reclass_sftime(NextMethod(), time_column_name = attr(data, "time_column")) 203 | } 204 | 205 | #' @rdname tidyverse 206 | #' @examples 207 | #' ## sample_n 208 | #' set.seed(234) 209 | #' x1 %>% 210 | #' sample_n(size = 10, replace = TRUE) 211 | #' 212 | sample_n.sftime <- function(tbl, size, replace = FALSE, weight = NULL, .env = parent.frame()) { 213 | reclass_sftime(NextMethod(), time_column_name = attr(tbl, "time_column")) 214 | } 215 | 216 | #' @rdname tidyverse 217 | #' @examples 218 | #' ## sample_frac 219 | #' x1 %>% 220 | #' sample_frac(size = 10, replace = TRUE) %>% 221 | #' sample_frac(size = 0.1, replace = FALSE) 222 | #' 223 | sample_frac.sftime <- function(tbl, size = 1, replace = FALSE, weight = NULL, .env = parent.frame()) { 224 | reclass_sftime(NextMethod(), time_column_name = attr(tbl, "time_column")) 225 | } 226 | 227 | #' @rdname tidyverse 228 | #' @examples 229 | #' ## nest 230 | #' x1 %>% 231 | #' nest(a1 = -time) 232 | #' 233 | nest.sftime <- function (.data, ...) { 234 | reclass_sftime(NextMethod(), time_column_name = attr(.data, "time_column")) 235 | } 236 | 237 | #' @name tidyverse 238 | #' @examples 239 | #' ## unnest 240 | #' x1 %>% 241 | #' mutate(a1 = list(1, c(1, 2), 5)) %>% 242 | #' unnest(a1) 243 | #' 244 | unnest.sftime = function(data, ..., .preserve = NULL) { 245 | reclass_sftime(NextMethod(), time_column_name = attr(data, "time_column")) 246 | } 247 | 248 | #' @rdname tidyverse 249 | #' @examples 250 | #' ## separate 251 | #' x1 %>% 252 | #' mutate(x = c(NA, "a.b", "a.d")) %>% 253 | #' separate(x, c("A", "B")) 254 | #' 255 | separate.sftime <- function(data, col, into, sep = "[^[:alnum:]]+", remove = TRUE, 256 | convert = FALSE, extra = "warn", fill = "warn", ...) { 257 | 258 | time_column_name <- attr(data, "time_column") 259 | class(data) <- setdiff(class(data), "sftime") 260 | 261 | # modified from sftime (tidyverse.R) 262 | if (!requireNamespace("rlang", quietly = TRUE)) 263 | stop("rlang required: install first?") 264 | col <- rlang::enquo(col) 265 | 266 | res <- tidyr::separate(data, !!col, into = into, 267 | sep = sep, remove = remove, convert = convert, extra = extra, fill = fill, ...) 268 | reclass_sftime(res, time_column_name = time_column_name) 269 | 270 | } 271 | 272 | #' @name tidyverse 273 | #' @examples 274 | #' ## unite 275 | #' x1 %>% 276 | #' mutate(x = c(NA, "a.b", "a.d")) %>% 277 | #' separate(x, c("A", "B")) %>% 278 | #' unite(x, c("A", "B")) 279 | #' 280 | unite.sftime <- function(data, col, ..., sep = "_", remove = TRUE) { 281 | reclass_sftime(NextMethod(), time_column_name = attr(data, "time_column")) 282 | } 283 | 284 | #' @rdname tidyverse 285 | #' @examples 286 | #' ## separate_rows 287 | #' x1 %>% 288 | #' mutate(z = c("1", "2,3,4", "5,6")) %>% 289 | #' separate_rows(z, convert = TRUE) 290 | #' 291 | separate_rows.sftime <- function(data, ..., sep = "[^[:alnum:]]+", convert = FALSE) { 292 | reclass_sftime(NextMethod(), time_column_name = attr(data, "time_column")) 293 | } 294 | 295 | # modified from https://github.com/r-spatial/sf/blob/9d3bcf864f77f651281e23cde6747d440fb54242/R/tidyverse.R: 296 | # This is currently only used in `bind_rows()` and `bind_cols()` 297 | # because sf overrides all default implementations 298 | dplyr_reconstruct.sftime <- function(data, template) { 299 | 300 | data <- NextMethod() 301 | time_column_name <- attr(template, "time_column") 302 | 303 | # if the sf object could not be reconstructed or there is no time column, return `data` as is 304 | if (! inherits(data, "sf") || ! time_column_name %in% names(data)) { 305 | data 306 | } else { 307 | st_as_sftime( 308 | data, 309 | time_column_name = time_column_name 310 | ) 311 | } 312 | 313 | } 314 | 315 | #' @rdname tidyverse 316 | #' @examples 317 | #' ## drop_na 318 | #' x1 %>% 319 | #' mutate(z = c(1, 2, NA)) %>% 320 | #' drop_na(z) 321 | #' 322 | #' x1 %>% 323 | #' mutate(z = c(1, NA, NA)) %>% 324 | #' drop_na(z) 325 | #' 326 | #' x1 %>% 327 | #' mutate(time = replace(time, 1, NA)) %>% 328 | #' drop_na(time) 329 | drop_na.sftime <- function(data, ...) { 330 | reclass_sftime(NextMethod(), time_column_name = attr(data, "time_column")) 331 | } 332 | 333 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r, include = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "man/figures/README-", 12 | out.width = "100%" 13 | ) 14 | ``` 15 | 16 | [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/r-spatial/sftime?branch=master&svg=true)](https://ci.appveyor.com/project/edzerpebesma/sftime) 17 | [![CRAN](https://www.r-pkg.org/badges/version/sftime)](https://cran.r-project.org/package=sftime) 18 | [![cran checks](https://badges.cranchecks.info/worst/sftime.svg)](https://cran.r-project.org/web/checks/check_results_sftime.html) 19 | 20 | # sftime 21 | 22 | `sftime` provides time extension for simple features in R. `sftime` is an extension to the [`sf`](https://github.com/r-spatial/sf) package. It allows to store spatial features which are accompanied by time information, similar to the [`stars`](https://github.com/r-spatial/stars/) package. 23 | 24 | `sftime` is a complement to the `stars` package: Whereas `stars` is dedicated to handle regular spatiotemporal data, where space and time represent array dimensions of data cubes, `sftime` provides a generic data format which can also handle irregular spatiotemporal data. 25 | 26 | Examples for such data include earthquakes, accidents, disease or death cases, lightning 27 | strikes, but also movement data which have further constraints. 28 | 29 | ## Installation 30 | 31 | You can install the CRAN version of the package with: 32 | 33 | ```{r installation-CRAN, eval=FALSE} 34 | install.packages("sftime") 35 | ``` 36 | 37 | You can install the development version of `sftime` from [GitHub](https://github.com/) with: 38 | 39 | ```{r installation-development, eval=FALSE} 40 | library(remotes) 41 | install_github("r-spatial/sftime") 42 | ``` 43 | 44 | 45 | ## Contributing 46 | 47 | * Contributions of all sorts are most welcome, issues and pull requests are the preferred ways of sharing them. 48 | * Please note that the sftime project is released with a [Contributor Code of Conduct](https://contributor-covenant.org/version/2/0/CODE_OF_CONDUCT.html). By contributing to this project, you agree to abide by its terms. 49 | 50 | ## Acknowledgment 51 | 52 | This project gratefully acknowledges financial [support](https://www.r-consortium.org/projects) from the 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [![AppVeyor Build 5 | Status](https://ci.appveyor.com/api/projects/status/github/r-spatial/sftime?branch=master&svg=true)](https://ci.appveyor.com/project/edzerpebesma/sftime) 6 | [![CRAN](https://www.r-pkg.org/badges/version/sftime)](https://cran.r-project.org/package=sftime) 7 | [![cran 8 | checks](https://badges.cranchecks.info/worst/sftime.svg)](https://cran.r-project.org/web/checks/check_results_sftime.html) 9 | 10 | # sftime 11 | 12 | `sftime` provides time extension for simple features in R. `sftime` is 13 | an extension to the [`sf`](https://github.com/r-spatial/sf) package. It 14 | allows to store spatial features which are accompanied by time 15 | information, similar to the 16 | [`stars`](https://github.com/r-spatial/stars/) package. 17 | 18 | `sftime` is a complement to the `stars` package: Whereas `stars` is 19 | dedicated to handle regular spatiotemporal data, where space and time 20 | represent array dimensions of data cubes, `sftime` provides a generic 21 | data format which can also handle irregular spatiotemporal data. 22 | 23 | Examples for such data include earthquakes, accidents, disease or death 24 | cases, lightning strikes, but also movement data which have further 25 | constraints. 26 | 27 | ## Installation 28 | 29 | You can install the CRAN version of the package with: 30 | 31 | ``` r 32 | install.packages("sftime") 33 | ``` 34 | 35 | You can install the development version of `sftime` from 36 | [GitHub](https://github.com/) with: 37 | 38 | ``` r 39 | library(remotes) 40 | install_github("r-spatial/sftime") 41 | ``` 42 | 43 | ## Contributing 44 | 45 | - Contributions of all sorts are most welcome, issues and pull requests 46 | are the preferred ways of sharing them. 47 | - Please note that the sftime project is released with a [Contributor 48 | Code of 49 | Conduct](https://contributor-covenant.org/version/2/0/CODE_OF_CONDUCT.html). 50 | By contributing to this project, you agree to abide by its terms. 51 | 52 | ## Acknowledgment 53 | 54 | This project gratefully acknowledges financial 55 | [support](https://www.r-consortium.org/projects) from the 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://r-spatial.github.io/sftime/ 2 | template: 3 | bootstrap: 5 4 | 5 | navbar: 6 | structure: 7 | right: github 8 | components: 9 | github: 10 | icon: fab fa-github fa-lg 11 | href: https://github.com/r-spatial/sftime/ 12 | articles: 13 | text: Articles 14 | menu: 15 | - text: "Introduction to the sftime package" 16 | href: articles/sftime.html 17 | - text: "r-spatial blog post introducing the sftime package" 18 | href: https://r-spatial.org/r/2022/04/12/sftime-1.html 19 | news: 20 | text: Changelog 21 | href: news/index.html 22 | 23 | 24 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # DO NOT CHANGE the "init" and "install" sections below 2 | 3 | # Download script file from GitHub 4 | init: 5 | ps: | 6 | $ErrorActionPreference = "Stop" 7 | Invoke-WebRequest http://raw.github.com/krlmlr/r-appveyor/master/scripts/appveyor-tool.ps1 -OutFile "..\appveyor-tool.ps1" 8 | Import-Module '..\appveyor-tool.ps1' 9 | 10 | install: 11 | ps: Bootstrap 12 | 13 | # Adapt as necessary starting from here 14 | 15 | build_script: 16 | - travis-tool.sh install_deps 17 | 18 | test_script: 19 | - travis-tool.sh run_tests 20 | 21 | on_failure: 22 | - 7z a failure.zip *.Rcheck\* 23 | - appveyor PushArtifact failure.zip 24 | 25 | artifacts: 26 | - path: '*.Rcheck\**\*.log' 27 | name: Logs 28 | 29 | - path: '*.Rcheck\**\*.out' 30 | name: Logs 31 | 32 | - path: '*.Rcheck\**\*.fail' 33 | name: Logs 34 | 35 | - path: '*.Rcheck\**\*.Rout' 36 | name: Logs 37 | 38 | - path: '\*_*.tar.gz' 39 | name: Bits 40 | 41 | - path: '\*_*.zip' 42 | name: Bits 43 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | This release should fix CRAN check notes required to be fixed before 2024-10-04. 2 | 3 | To this end, we removed the old `tests` directory which has no purpose any more 4 | and caused the check note. 5 | 6 | In addition, this submission adds some new functions (`tidyr::drop_na()` method, `dplyr::dplyr_reconstruct()` method, methods to convert objects of other classes to `sftime` objects) and fixes a minor bug in `st_time<-.sftime`. 7 | 8 | 9 | ## R CMD check results 10 | 11 | 0 errors | 0 warnings | 0 notes 12 | 13 | 14 | ## revdepcheck results 15 | 16 | We checked 6 reverse dependencies (5 from CRAN + 1 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package. 17 | 18 | * We saw 0 new problems 19 | * We failed to check 0 packages 20 | -------------------------------------------------------------------------------- /man/bind.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/bind.R 3 | \name{bind} 4 | \alias{bind} 5 | \alias{rbind.sftime} 6 | \alias{cbind.sftime} 7 | \title{Bind rows (features) of \code{sftime} objects} 8 | \usage{ 9 | \method{rbind}{sftime}(..., deparse.level = 1) 10 | 11 | \method{cbind}{sftime}(..., deparse.level = 1, sf_column_name = NULL, tc_column_name = NULL) 12 | } 13 | \arguments{ 14 | \item{...}{Objects to bind; note that for the \code{rbind} and \code{cbind} 15 | methods, all objects have to be of class \code{sftime}; see 16 | \code{\link{dotsMethods}}.} 17 | 18 | \item{deparse.level}{An integer value; see \code{\link{rbind}}.} 19 | 20 | \item{sf_column_name}{Character value; specifies the active geometry column; 21 | passed on to \code{\link{st_sftime}}.} 22 | 23 | \item{tc_column_name}{Character value; specifies active time column; passed 24 | on to \code{\link{st_sftime}}.} 25 | } 26 | \value{ 27 | \code{rbind} combines all \code{sftime} objects in \code{...} 28 | row-wise and returns the combined \code{sftime} object. 29 | 30 | \code{cbind} combines all \code{sftime} objects in \code{...} 31 | column-wise and returns the combined \code{sftime} object. When called with 32 | multiple \code{sftime} objects warns about multiple time and geometry columns 33 | present when the time and geometry columns to use are not specified by using 34 | arguments \code{tc_column_name} and \code{sf_column_name}; see also 35 | \link{st_sftime}. 36 | } 37 | \description{ 38 | Bind rows (features) of \code{sftime} objects 39 | 40 | Bind columns (variables) of \code{sftime} objects 41 | } 42 | \details{ 43 | Both \code{rbind} and \code{cbind} have non-standard method dispatch 44 | (see \link[base]{cbind}): the \code{rbind} or \code{cbind} method for 45 | \code{sftime} objects is only called when all arguments to be combined are of 46 | class \code{sftime}. 47 | 48 | If you need to \code{cbind} e.g. a \code{data.frame} to an \code{sf}, 49 | use \code{\link{data.frame}} directly and use \code{\link{st_sftime}} on its 50 | result, or use \code{\link[dplyr:bind]{bind_cols}}; see examples. 51 | } 52 | \examples{ 53 | g1 <- st_sfc(st_point(1:2)) 54 | x1 <- st_sftime(a = 3, geometry = g1, time = Sys.time()) 55 | 56 | g2 <- st_sfc(st_point(c(4, 6))) 57 | x2 <- st_sftime(a = 4, geometry = g2, time = Sys.time()) 58 | 59 | rbind(x1, x2) # works because both tc1 and tc2 have the same class 60 | 61 | \dontrun{ 62 | st_time(x2) <- 1 63 | rbind(x1, x2) # error because both tc1 and tc2 do not have the same class 64 | } 65 | 66 | cbind(x1, x2) 67 | 68 | if (require(dplyr)) { 69 | # returns a data frame because names of sf and time column are modified: 70 | dplyr::bind_cols(x1, x2) 71 | 72 | # returns an sf object because the name of the time column is modified: 73 | dplyr::bind_cols(x1, x2 \%>\% sf::st_drop_geometry()) 74 | 75 | # returns an sftime object because names of sf and time column are both 76 | # preserved: 77 | dplyr::bind_cols(x1, x2 \%>\% st_drop_time() \%>\% sf::st_drop_geometry()) 78 | } 79 | 80 | df <- data.frame(x = 3) 81 | st_sftime(data.frame(x1, df)) 82 | 83 | } 84 | -------------------------------------------------------------------------------- /man/geos_binary_ops.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geom-transformers.R 3 | \name{geos_binary_ops} 4 | \alias{geos_binary_ops} 5 | \alias{st_intersection.sftime} 6 | \alias{st_difference.sftime} 7 | \alias{st_sym_difference.sftime} 8 | \title{Geometric operations on pairs of simple feature geometry sets (including \code{sftime} objects)} 9 | \usage{ 10 | \method{st_intersection}{sftime}(x, y, ...) 11 | 12 | \method{st_difference}{sftime}(x, y, ...) 13 | 14 | \method{st_sym_difference}{sftime}(x, y, ...) 15 | } 16 | \arguments{ 17 | \item{x}{object of class \code{sftime}, \code{sf}, \code{sfc} or \code{sfg}.} 18 | 19 | \item{y}{object of class \code{sftime}, \code{sf}, \code{sfc} or \code{sfg}.} 20 | 21 | \item{...}{See \code{\link[sf:geos_binary_ops]{geos_binary_ops}}.} 22 | } 23 | \value{ 24 | The intersection, difference or symmetric difference between two sets 25 | of geometries. 26 | The returned object has the same class as that of the first argument 27 | (\code{x}) with the non-empty geometries resulting from applying the 28 | operation to all geometry pairs in \code{x} and \code{y}. In case \code{x} 29 | is of class \code{sf} or \code{sftime}, the matching attributes of the 30 | original object(s) are added. The \code{sfc} geometry list-column returned 31 | carries an attribute \code{idx}, which is an \code{n}-by-2 matrix with every 32 | row the index of the corresponding entries of \code{x} and \code{y}, 33 | respectively. 34 | } 35 | \description{ 36 | Geometric operations on pairs of simple feature geometry sets (including \code{sftime} objects) 37 | 38 | Intersection 39 | 40 | Difference 41 | } 42 | \details{ 43 | \code{st_intersection}: When called with a missing \code{y}, the 44 | \code{sftime} method for \code{st_intersection} returns an \code{sftime} 45 | object with attributes taken from the contributing feature with lowest index; 46 | two fields are added: 47 | \describe{ 48 | \item{\code{n.overlaps}}{The number of overlapping features in \code{x}.} 49 | \item{\code{origins}}{A list-column with indexes of all overlapping 50 | features.} 51 | } 52 | 53 | \code{st_difference}: When \code{st_difference} is called with a 54 | single argument, overlapping areas are erased from geometries that are 55 | indexed at greater numbers in the argument to \code{x}; geometries that are 56 | empty or contained fully inside geometries with higher priority are removed 57 | entirely. 58 | } 59 | \examples{ 60 | g <- st_sfc(st_point(c(1, 2)), st_point(c(1, 3)), st_point(c(2, 3)), 61 | st_point(c(2, 1)), st_point(c(3, 1))) 62 | tc <- Sys.time() + 1:5 63 | x1 <- st_sftime(a = 1:5, g, time = tc) 64 | x2 <- st_buffer(x1, dist = 1) 65 | 66 | ## intersection 67 | 68 | # only x provided (no y) 69 | plot(st_intersection(x2)) 70 | 71 | # with arguments x and y provided 72 | plot(st_intersection(x2, x1)) 73 | 74 | ## difference 75 | 76 | # only x provided (no y) 77 | plot(st_difference(x2)) 78 | 79 | # with arguments x and y provided 80 | plot(st_difference(x2, x1)) 81 | 82 | ## symmetric difference 83 | plot(st_sym_difference(x1, x2)) 84 | 85 | } 86 | -------------------------------------------------------------------------------- /man/geos_combine.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geom-transformers.R 3 | \name{geos_combine} 4 | \alias{geos_combine} 5 | \alias{st_union.sftime} 6 | \title{Combine or union feature geometries (including \code{sftime} objects)} 7 | \usage{ 8 | \method{st_union}{sftime}(x, y, ..., by_feature = FALSE, is_coverage = FALSE) 9 | } 10 | \arguments{ 11 | \item{x}{An object of class \code{sftime}, \code{sf}, \code{sfc} or 12 | \code{sfg}.} 13 | 14 | \item{y}{An object of class \code{sftime}, \code{sf}, \code{sfc} or 15 | \code{sfg} (optional).} 16 | 17 | \item{...}{See \code{\link[sf:geos_combine]{geos_combine}}.} 18 | 19 | \item{by_feature}{See \code{\link[sf:geos_combine]{geos_combine}}.} 20 | 21 | \item{is_coverage}{See \code{\link[sf:geos_combine]{geos_combine}}.} 22 | } 23 | \value{ 24 | If \code{y} is missing, \code{st_union(x)} returns a single geometry 25 | with resolved boundaries, else the geometries for all unioned pairs of 26 | \code{x[i]} and \code{y[j]}. 27 | } 28 | \description{ 29 | Combine or union feature geometries (including \code{sftime} objects) 30 | } 31 | \details{ 32 | See \code{\link[sf:geos_combine]{geos_combine}}. 33 | } 34 | \examples{ 35 | # union simple features in an sftime object 36 | g <- st_sfc(st_point(c(1, 2)), st_point(c(1, 3)), st_point(c(2, 3)), 37 | st_point(c(2, 1)), st_point(c(3, 1))) 38 | tc <- Sys.time() + 1:5 39 | x <- st_sftime(a = 1:5, g, time = tc) 40 | 41 | # only x provided (no y) 42 | plot(st_union(st_buffer(x, dist = 1))) 43 | 44 | # with arguments x and y provided 45 | plot(st_union(st_buffer(x, dist = 1), st_buffer(x, dist = 0.5)), "a") 46 | 47 | } 48 | -------------------------------------------------------------------------------- /man/is_sortable.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sftime.R 3 | \name{is_sortable} 4 | \alias{is_sortable} 5 | \title{Checks whether a vector or list is sortable} 6 | \usage{ 7 | is_sortable(x) 8 | } 9 | \arguments{ 10 | \item{x}{The object to check.} 11 | } 12 | \value{ 13 | \code{TRUE} if \code{x} passes the check, else \code{FALSE}. 14 | } 15 | \description{ 16 | Checks whether a vector or list is sortable. This is the condition for a 17 | vector to be usable as time column in a \code{sftime} object. 18 | } 19 | \details{ 20 | Checks whether the provided object can be handled by 21 | \code{\link{order}}. A couple of basic types are whitelisted. However, custom 22 | types can be defined when they provide a dedicated generic to \link{xtfrm}. 23 | Note that a \code{list} can only be sorted with \link{atomic} values. See the 24 | examples below for a template. 25 | } 26 | \examples{ 27 | x <- Sys.time() + 5:1 * 3600 * 24 28 | sort(x) 29 | is_sortable(x) 30 | 31 | } 32 | \keyword{internal} 33 | -------------------------------------------------------------------------------- /man/plot.sftime.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot.R 3 | \name{plot.sftime} 4 | \alias{plot.sftime} 5 | \alias{plot} 6 | \title{Plots an \code{sftime} object} 7 | \usage{ 8 | \method{plot}{sftime}(x, y, ..., number = 6, tcuts) 9 | } 10 | \arguments{ 11 | \item{x}{The \code{\link[=st_sftime]{sftime}} object to be plotted.} 12 | 13 | \item{y}{A character value; The variable name to be plotted; if missing, the 14 | first variable is plotted.} 15 | 16 | \item{...}{Additional arguments; Passed on to \code{\link[sf:plot]{plot.sf}}.} 17 | 18 | \item{number}{A numeric value; The number of panels to be plotted, cannot be 19 | larger than the number of timestamps; ignored when \code{tcuts} is provided.} 20 | 21 | \item{tcuts}{predefined temporal ranges assigned to each map; if missing, 22 | will be determined as equal spans according to \code{number}.} 23 | } 24 | \value{ 25 | Returns \code{NULL} and creates as side effect a plot for \code{x}. 26 | } 27 | \description{ 28 | \code{plot.sftime} 29 | } 30 | \examples{ 31 | set.seed(123) 32 | coords <- matrix(runif(100), ncol = 2) 33 | g <- st_sfc(lapply(1:50, function(i) st_point(coords[i, ]) )) 34 | sft <- st_sftime(a = 1:50, g, time = as.POSIXct("2020-09-01 00:00:00") + 0:49 * 3600 * 6) 35 | 36 | plot(sft) 37 | 38 | } 39 | -------------------------------------------------------------------------------- /man/print.sftime.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sftime.R 3 | \name{print.sftime} 4 | \alias{print.sftime} 5 | \title{Prints an \code{sftime} object} 6 | \usage{ 7 | \method{print}{sftime}(x, ..., n = getOption("sf_max_print", default = 10)) 8 | } 9 | \arguments{ 10 | \item{x}{An object of class \code{sftime}.} 11 | 12 | \item{...}{Currently unused arguments, for compatibility.} 13 | 14 | \item{n}{Numeric value; maximum number of printed elements.} 15 | } 16 | \value{ 17 | \code{x} (invisible). 18 | } 19 | \description{ 20 | Prints an \code{sftime} object 21 | } 22 | \examples{ 23 | g <- st_sfc(st_point(c(1, 2)), st_point(c(1, 3)), st_point(c(2, 3)), 24 | st_point(c(2, 1)), st_point(c(3, 1))) 25 | tc <- Sys.time() + 1:5 26 | x <- st_sftime(a = 1:5, g, time = tc) 27 | print(x) 28 | print(x[0, ]) 29 | 30 | } 31 | -------------------------------------------------------------------------------- /man/st_as_sftime.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sftime.R 3 | \name{st_as_sftime} 4 | \alias{st_as_sftime} 5 | \alias{st_as_sftime.ST} 6 | \alias{st_as_sftime.Track} 7 | \alias{st_as_sftime.Tracks} 8 | \alias{st_as_sftime.TracksCollection} 9 | \alias{st_as_sftime.sftime} 10 | \alias{st_as_sftime.sf} 11 | \alias{st_as_sftime.stars} 12 | \alias{st_as_sftime.data.frame} 13 | \alias{st_as_sftime.ppp} 14 | \alias{st_as_sftime.psp} 15 | \alias{st_as_sftime.lpp} 16 | \alias{st_as_sftime.sftrack} 17 | \alias{st_as_sftime.sftraj} 18 | \alias{st_as_sftime.cubble_df} 19 | \title{Convert a foreign object to an \code{sftime} object} 20 | \usage{ 21 | st_as_sftime(x, ...) 22 | 23 | \method{st_as_sftime}{ST}(x, ...) 24 | 25 | \method{st_as_sftime}{Track}(x, ...) 26 | 27 | \method{st_as_sftime}{Tracks}(x, ...) 28 | 29 | \method{st_as_sftime}{TracksCollection}(x, ...) 30 | 31 | \method{st_as_sftime}{sftime}(x, ...) 32 | 33 | \method{st_as_sftime}{sf}(x, ..., time_column_name = NULL) 34 | 35 | \method{st_as_sftime}{stars}(x, ..., long = TRUE, time_column_name = NULL) 36 | 37 | \method{st_as_sftime}{data.frame}( 38 | x, 39 | ..., 40 | agr = NA_agr_, 41 | coords, 42 | wkt, 43 | dim = "XYZ", 44 | remove = TRUE, 45 | na.fail = TRUE, 46 | sf_column_name = NULL, 47 | time_column_name = NULL, 48 | time_column_last = FALSE 49 | ) 50 | 51 | \method{st_as_sftime}{ppp}(x, ..., time_column_name) 52 | 53 | \method{st_as_sftime}{psp}(x, ..., time_column_name) 54 | 55 | \method{st_as_sftime}{lpp}(x, ..., time_column_name) 56 | 57 | \method{st_as_sftime}{sftrack}(x, ...) 58 | 59 | \method{st_as_sftime}{sftraj}(x, ...) 60 | 61 | \method{st_as_sftime}{cubble_df}(x, ..., sfc = NULL, crs, silent = FALSE) 62 | } 63 | \arguments{ 64 | \item{x}{An object to be converted into an object of class 65 | \code{\link[=st_sftime]{sftime}}.} 66 | 67 | \item{...}{Further arguments passed to methods.} 68 | 69 | \item{time_column_name}{A character value; name of the active time column. In 70 | case there is more than one and \code{time_column_name} is \code{NULL}, the 71 | first one is taken.} 72 | 73 | \item{long}{A logical value; See \code{\link[stars:st_as_sf]{st_as_sf}}. 74 | Typically, \code{long} should be set to \code{TRUE} since time information 75 | typically is a dimension of a \code{stars} object.} 76 | 77 | \item{agr}{A character vector; see the details section of \code{\link[sf]{st_sf}}.} 78 | 79 | \item{coords}{In case of point data: names or numbers of the numeric columns 80 | holding coordinates.} 81 | 82 | \item{wkt}{The name or number of the character column that holds WKT encoded 83 | geometries.} 84 | 85 | \item{dim}{Passed on to \code{\link[sf]{st_point}} (only when argument 86 | \code{coords} is given).} 87 | 88 | \item{remove}{A logical value; when \code{coords} or \code{wkt} is given, 89 | remove these columns from \code{x}?} 90 | 91 | \item{na.fail}{A logical value; if \code{TRUE}, raise an error if coordinates 92 | contain missing values.} 93 | 94 | \item{sf_column_name}{A character value; name of the active list-column with 95 | simple feature geometries; in case there is more than one and 96 | \code{sf_column_name} is \code{NULL}, the first one is taken.} 97 | 98 | \item{time_column_last}{A logical value; if \code{TRUE}, the active time column is 99 | always put last, otherwise column order is left unmodified. If both \code{sfc_last} 100 | and \code{time_column_last} are \code{TRUE}, the active time column is put last.} 101 | 102 | \item{sfc}{object of class \code{sfc} (see package sf)} 103 | 104 | \item{crs}{Coordinate reference system, something suitable as input to 105 | \code{\link[sf]{st_crs}}.} 106 | 107 | \item{silent}{logical; suppress message?} 108 | } 109 | \value{ 110 | \code{x} converted to an \code{sftime} object. 111 | 112 | \code{st_as_sftime.Tracks} furthermore adds a column 113 | \code{track_name} with the names of the \code{tracks} slot of the input 114 | \code{Tracks} object. 115 | 116 | \code{st_as_sftime.TracksCollection} furthermore adds the columns 117 | \code{tracks_name} with the names of the \code{tracksCollection} slot and 118 | \code{track_name} with the names of the \code{tracks} slot of the input 119 | \code{Tracks} object. 120 | } 121 | \description{ 122 | Convert a foreign object to an \code{sftime} object 123 | } 124 | \examples{ 125 | # modified from spacetime: 126 | library(sp) 127 | library(spacetime) 128 | 129 | sp <- cbind(x = c(0,0,1), y = c(0,1,1)) 130 | row.names(sp) <- paste("point", 1:nrow(sp), sep="") 131 | sp <- SpatialPoints(sp) 132 | time <- as.POSIXct("2010-08-05") + 3600 * (10:12) 133 | x <- STI(sp, time) 134 | 135 | st_as_sftime(x) 136 | 137 | # convert a Track object from package trajectories to an sftime object 138 | library(trajectories) 139 | x1_Track <- trajectories::rTrack(n = 100) 140 | x1_Track@data$speed <- sort(rnorm(length(x1_Track))) 141 | x1_sftime <- st_as_sftime(x1_Track) 142 | 143 | # convert a Tracks object from package trajectories to an sftime object 144 | x2_Tracks <- trajectories::rTracks(m = 6) 145 | x2_sftime <- st_as_sftime(x2_Tracks) 146 | 147 | # convert a TracksCollection object from package trajectories to an sftime object 148 | x3_TracksCollection <- trajectories::rTracksCollection(p = 2, m = 3, n = 50) 149 | x3_sftime <- st_as_sftime(x3_TracksCollection) 150 | 151 | # convert an sftime object to an sftime object 152 | st_as_sftime(x3_sftime) 153 | 154 | # convert an sf object to an sftime object 155 | g <- st_sfc(st_point(c(1, 2)), st_point(c(1, 3)), st_point(c(2, 3)), 156 | st_point(c(2, 1)), st_point(c(3, 1))) 157 | x4_sf <- st_sf(a = 1:5, g, time = Sys.time() + 1:5) 158 | x4_sftime <- st_as_sftime(x4_sf) 159 | 160 | # convert a Tracks object from package trajectories to an sftime object 161 | x5_stars <- stars::read_stars(system.file("nc/bcsd_obs_1999.nc", package = "stars")) 162 | x5_sftime <- st_as_sftime(x5_stars, time_column_name = "time") 163 | 164 | # this requires some thought to not accidentally drop time dimensions. For 165 | # example, setting `merge = TRUE` will drop the time dimension and thus throw 166 | # an error: 167 | \dontrun{ 168 | x5_sftime <- st_as_sftime(x5_stars, merge = TRUE, time_column_name = "time") 169 | } 170 | 171 | # convert a data frame to an sftime object 172 | x5_df <- 173 | data.frame(a = 1:5, g, time = Sys.time() + 1:5, stringsAsFactors = FALSE) 174 | x5_sftime <- st_as_sftime(x5_df) 175 | 176 | # convert a ppp object to an sftime object (modified from the sf package) 177 | if (require(spatstat.geom)) { 178 | st_as_sftime(gorillas, time_column_name = "date") 179 | } 180 | 181 | # convert a psp object to an sftime object (modified from the spatstat.geom 182 | # package) 183 | if (require(spatstat.geom)) { 184 | # modified from spatstat.geom: 185 | x_psp <- 186 | psp( 187 | runif(10), runif(10), runif(10), runif(10), window=owin(), 188 | marks = data.frame(time = Sys.time() + 1:10) 189 | ) 190 | st_as_sftime(x_psp, time_column_name = "time") 191 | } 192 | 193 | # convert an lpp object to an sftime object (modified from the 194 | # spatstat.linnet package) 195 | if (require(spatstat.geom) && require(spatstat.linnet)) { 196 | # modified from spatstat.linnet: 197 | 198 | # letter 'A' 199 | v <- spatstat.geom::ppp(x=(-2):2, y=3*c(0,1,2,1,0), c(-3,3), c(-1,7)) 200 | edg <- cbind(1:4, 2:5) 201 | edg <- rbind(edg, c(2,4)) 202 | letterA <- spatstat.linnet::linnet(v, edges=edg) 203 | 204 | # points on letter A 205 | xx <- 206 | spatstat.geom::ppp( 207 | x=c(-1.5,0,0.5,1.5), y=c(1.5,3,4.5,1.5), 208 | marks = data.frame(time = Sys.time() + 1:4, a = 1:4), 209 | window = spatstat.geom::owin( 210 | xrange = range(c(-1.5,0,0.5,1.5)), 211 | yrange = range(c(1.5,3,4.5,1.5))) 212 | ) 213 | x_lpp <- spatstat.linnet::lpp(xx, letterA) 214 | 215 | # convert to sftime 216 | st_as_sftime(x_lpp, time_column_name = "time") 217 | } 218 | 219 | # convert an sftrack object to an sftime object (modified from sftrack) 220 | if (require(sftrack)) { 221 | 222 | # get an sftrack object 223 | data("raccoon") 224 | 225 | raccoon$timestamp <- as.POSIXct(raccoon$timestamp, "EST") 226 | 227 | burstz <- 228 | list(id = raccoon$animal_id, month = as.POSIXlt(raccoon$timestamp)$mon) 229 | 230 | x_sftrack <- 231 | as_sftrack(raccoon, 232 | group = burstz, time = "timestamp", 233 | error = NA, coords = c("longitude", "latitude") 234 | ) 235 | 236 | # convert to sftime 237 | st_as_sftime(x_sftrack) 238 | } 239 | 240 | # convert an sftraj object to an sftime object (modified from sftrack) 241 | if (require(sftrack)) { 242 | 243 | # get an sftrack object 244 | data("raccoon") 245 | 246 | raccoon$timestamp <- as.POSIXct(raccoon$timestamp, "EST") 247 | 248 | burstz <- 249 | list(id = raccoon$animal_id, month = as.POSIXlt(raccoon$timestamp)$mon) 250 | 251 | x_sftraj <- 252 | as_sftraj(raccoon, 253 | time = "timestamp", 254 | error = NA, coords = c("longitude", "latitude"), 255 | group = burstz 256 | ) 257 | 258 | # convert to sftime 259 | st_as_sftime(x_sftraj) 260 | } 261 | 262 | # convert a cubble_df object from package cubble to an sftime object 263 | if (requireNamespace("cubble", quietly = TRUE, versionCheck = "0.3.0")) { 264 | 265 | # get a cubble_df object 266 | data("climate_aus", package = "cubble") 267 | 268 | # convert to sftime 269 | climate_aus_sftime <- 270 | st_as_sftime(climate_aus[1:4, ]) 271 | 272 | climate_aus_sftime <- 273 | st_as_sftime(cubble::face_temporal(climate_aus)[1:4, ]) 274 | 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /man/st_cast.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/st_cast.R 3 | \name{st_cast} 4 | \alias{st_cast} 5 | \alias{st_cast.sftime} 6 | \title{Cast geometry to another type: either simplify, or cast explicitly} 7 | \usage{ 8 | \method{st_cast}{sftime}(x, to, ..., warn = TRUE, do_split = TRUE) 9 | } 10 | \arguments{ 11 | \item{x}{An object of class \code{sftime}.} 12 | 13 | \item{to}{character; target type, if missing, simplification is tried; when \code{x} is of type \code{sfg} (i.e., a single geometry) then \code{to} needs to be specified.} 14 | 15 | \item{...}{ignored} 16 | 17 | \item{warn}{logical; if \code{TRUE}, warn if attributes are assigned to sub-geometries} 18 | 19 | \item{do_split}{logical; if \code{TRUE}, allow splitting of geometries in sub-geometries} 20 | } 21 | \value{ 22 | \code{x} with changed geometry type. 23 | } 24 | \description{ 25 | Cast geometry to another type: either simplify, or cast explicitly 26 | } 27 | \examples{ 28 | # cast from POINT to LINESTRING 29 | g <- st_sfc(st_point(1:2), st_point(c(2, 4))) 30 | time <- Sys.time() 31 | x <- 32 | st_sftime(a = 3:4, g, time = time) \%>\% 33 | dplyr::group_by(time) \%>\% 34 | dplyr::summarize(do_union = TRUE) \%>\% 35 | st_cast(to = "LINESTRING") 36 | } 37 | -------------------------------------------------------------------------------- /man/st_crop.sftime.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/crop.R 3 | \name{st_crop.sftime} 4 | \alias{st_crop.sftime} 5 | \title{Crop an \code{sftime} object to a specific rectangle} 6 | \usage{ 7 | \method{st_crop}{sftime}(x, y, ...) 8 | } 9 | \arguments{ 10 | \item{x}{An object of class \code{sftime}.} 11 | 12 | \item{y}{A numeric vector with named elements \code{xmin}, \code{ymin}, 13 | \code{xmax} and \code{ymax}, or an object of class \code{bbox}, or an object 14 | for which there is an \code{\link[sf:st_bbox]{st_bbox}} method to convert it 15 | to a \code{bbox} object.} 16 | 17 | \item{...}{Additional arguments; Ignored.} 18 | } 19 | \value{ 20 | \code{x} cropped using \code{y}. 21 | } 22 | \description{ 23 | Crop an \code{sftime} object to a specific rectangle 24 | } 25 | \details{ 26 | See \code{\link[sf:st_crop]{st_crop}}. 27 | } 28 | \examples{ 29 | # modified from sf: 30 | box <- c(xmin = 0, ymin = 0, xmax = 1, ymax = 1) 31 | pol <- sf::st_sfc(sf::st_buffer(sf::st_point(c(0.5, 0.5)), 0.6)) 32 | pol_sftime <- st_sftime(a = 1, geom = pol, time = Sys.time() + 1:2 * 1000) 33 | 34 | pol_sftime_cropped <- sf::st_crop(pol_sftime, sf::st_bbox(box)) 35 | 36 | class(pol_sftime_cropped) 37 | plot(pol_sftime_cropped) 38 | } 39 | -------------------------------------------------------------------------------- /man/st_geometry.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/st_geometry.R 3 | \name{st_geometry} 4 | \alias{st_geometry} 5 | \alias{st_drop_geometry.sftime} 6 | \title{Drops the geometry column of \code{sftime} objects} 7 | \usage{ 8 | \method{st_drop_geometry}{sftime}(x, ...) 9 | } 10 | \arguments{ 11 | \item{x}{An \code{sftime} object.} 12 | 13 | \item{...}{ignored} 14 | } 15 | \value{ 16 | \code{x} without geometry column and without \code{sftime} and 17 | \code{sf} class. 18 | } 19 | \description{ 20 | Drops the geometry column of an \code{sftime} object. This will also drop 21 | the \code{sftime} class attribute and \code{time_column} attribute. 22 | } 23 | \examples{ 24 | # dropping the geometry column will also drop the `sftime` class: 25 | g <- st_sfc(st_point(1:2)) 26 | time <- Sys.time() 27 | x <- st_sftime(a = 3, g, time = time) 28 | st_drop_geometry(x) 29 | 30 | } 31 | -------------------------------------------------------------------------------- /man/st_join.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/join.R 3 | \name{st_join} 4 | \alias{st_join} 5 | \alias{st_join.sftime} 6 | \alias{st_filter.sftime} 7 | \title{Spatial join, spatial filter for \code{sftime} objects} 8 | \usage{ 9 | \method{st_join}{sftime}( 10 | x, 11 | y, 12 | join = st_intersects, 13 | ..., 14 | suffix = c(".x", ".y"), 15 | left = TRUE, 16 | largest = FALSE 17 | ) 18 | 19 | \method{st_filter}{sftime}(x, y, ..., .predicate = st_intersects) 20 | } 21 | \arguments{ 22 | \item{x}{An object of class \code{sftime} or \code{sf}.} 23 | 24 | \item{y}{An object of class \code{sftime} or \code{sf}.} 25 | 26 | \item{join}{A geometry predicate function with the same profile as 27 | \code{\link[sf:geos_binary_pred]{st_intersects}}; see details.} 28 | 29 | \item{...}{for \code{st_join}: arguments passed on to the \code{join} function or to \code{st_intersection} when \code{largest} is \code{TRUE}; for \code{st_filter} arguments passed on to the \code{.predicate} function, e.g. \code{prepared}, or a pattern for \link[sf]{st_relate}} 30 | 31 | \item{suffix}{length 2 character vector; see \link[base]{merge}} 32 | 33 | \item{left}{logical; if \code{TRUE} return the left join, otherwise an inner join; see details. 34 | see also \link[dplyr:mutate-joins]{left_join}} 35 | 36 | \item{largest}{logical; if \code{TRUE}, return \code{x} features augmented with the fields of \code{y} that have the largest overlap with each of the features of \code{x}; see https://github.com/r-spatial/sf/issues/578} 37 | 38 | \item{.predicate}{A geometry predicate function with the same profile as 39 | \code{\link[sf:geos_binary_pred]{st_intersects}}; see details.} 40 | } 41 | \value{ 42 | An object of class \code{sftime}, joined based on geometry. 43 | } 44 | \description{ 45 | Spatial join, spatial filter for \code{sftime} objects 46 | } 47 | \details{ 48 | Alternative values for argument \code{join} are: 49 | \itemize{ 50 | \item \link[sf:geos_binary_pred]{st_contains_properly} 51 | \item \link[sf:geos_binary_pred]{st_contains} 52 | \item \link[sf:geos_binary_pred]{st_covered_by} 53 | \item \link[sf:geos_binary_pred]{st_covers} 54 | \item \link[sf:geos_binary_pred]{st_crosses} 55 | \item \link[sf:geos_binary_pred]{st_disjoint} 56 | \item \link[sf:geos_binary_pred]{st_equals_exact} 57 | \item \link[sf:geos_binary_pred]{st_equals} 58 | \item \link[sf:geos_binary_pred]{st_is_within_distance} 59 | \item \link[sf:geos_binary_pred]{st_nearest_feature} 60 | \item \link[sf:geos_binary_pred]{st_overlaps} 61 | \item \link[sf:geos_binary_pred]{st_touches} 62 | \item \link[sf:geos_binary_pred]{st_within} 63 | \item any user-defined function of the same profile as the above 64 | } 65 | A left join returns all records of the \code{x} object with \code{y} fields 66 | for non-matched records filled with \code{NA} values; an inner join returns 67 | only records that spatially match. 68 | } 69 | \examples{ 70 | g1 <- st_sfc(st_point(c(1,1)), st_point(c(2,2)), st_point(c(3,3))) 71 | x1 <- st_sftime(a = 1:3, geometry = g1, time = Sys.time()) 72 | 73 | g2 <- st_sfc(st_point(c(10,10)), st_point(c(2,2)), st_point(c(2,2)), st_point(c(3,3))) 74 | x2 <- st_sftime(a = 11:14, geometry = g2, time = Sys.time()) 75 | 76 | ## st_join 77 | 78 | # left spatial join with st_intersects 79 | st_join(x1, x2) 80 | 81 | # inner spatial join with st_intersects 82 | st_join(x1, x2, left = FALSE) 83 | 84 | ## st_filter 85 | 86 | st_filter(x1, x2) 87 | st_filter(x2, x1) 88 | 89 | } 90 | -------------------------------------------------------------------------------- /man/st_sftime.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sftime.R 3 | \name{st_sftime} 4 | \alias{st_sftime} 5 | \alias{[.sftime} 6 | \alias{[[<-.sftime} 7 | \alias{$<-.sftime} 8 | \title{Construct an \code{sftime} object from all its components} 9 | \usage{ 10 | st_sftime( 11 | ..., 12 | agr = sf::NA_agr_, 13 | row.names, 14 | stringsAsFactors = TRUE, 15 | crs, 16 | precision, 17 | sf_column_name = NULL, 18 | time_column_name = NULL, 19 | check_ring_dir = FALSE, 20 | sfc_last = TRUE, 21 | time_column_last = TRUE 22 | ) 23 | 24 | \method{[}{sftime}(x, i, j, ..., drop = FALSE, op = sf::st_intersects) 25 | 26 | \method{[[}{sftime}(x, i) <- value 27 | 28 | \method{$}{sftime}(x, i) <- value 29 | } 30 | \arguments{ 31 | \item{...}{Column elements to be binded into an \code{sftime} object or a 32 | single \code{list} or \code{data.frame} with such columns. At least one of 33 | these columns shall be a geometry list-column of class \code{sfc} and one 34 | shall be a time column (to be specified with \code{time_column_name}).} 35 | 36 | \item{agr}{A character vector; see details below.} 37 | 38 | \item{row.names}{row.names for the created \code{sf} object.} 39 | 40 | \item{stringsAsFactors}{A logical value; see 41 | \code{\link[sf]{st_read}}.} 42 | 43 | \item{crs}{Coordinate reference system, something suitable as input to 44 | \code{\link[sf]{st_crs}}.} 45 | 46 | \item{precision}{A numeric value; see 47 | \code{\link[sf]{st_as_binary}}.} 48 | 49 | \item{sf_column_name}{A character value; name of the active list-column with 50 | simple feature geometries; in case there is more than one and 51 | \code{sf_column_name} is \code{NULL}, the first one is taken.} 52 | 53 | \item{time_column_name}{A character value; name of the active 54 | time column. In case \code{time_column_name} is \code{NULL}, the first 55 | \code{\link{POSIXct}} column is taken. If there is no \code{POSIXct} column, 56 | the first \code{\link{Date}} column is taken.} 57 | 58 | \item{check_ring_dir}{A logical value; see \code{\link[sf]{st_read}}.} 59 | 60 | \item{sfc_last}{A logical value; if \code{TRUE}, \code{sfc} columns are 61 | always put last, otherwise column order is left unmodified.} 62 | 63 | \item{time_column_last}{A logical value; if \code{TRUE}, the active time column is 64 | always put last, otherwise column order is left unmodified. If both \code{sfc_last} 65 | and \code{time_column_last} are \code{TRUE}, the active time column is put last.} 66 | 67 | \item{x}{An object of class \code{sf}.} 68 | 69 | \item{i}{Record selection, see \link{[.data.frame}} 70 | 71 | \item{j}{Variable selection, see \link{[.data.frame}} 72 | 73 | \item{drop}{A logical value, default \code{FALSE}; if \code{TRUE} drop the 74 | geometry column and return a \code{data.frame}, else make the geometry sticky 75 | and return an \code{sf} object.} 76 | 77 | \item{op}{A function; geometrical binary predicate function to apply when 78 | \code{i} is a simple feature object.} 79 | 80 | \item{value}{An object to insert into \code{x} or with which to rename 81 | columns of \code{x}.} 82 | } 83 | \value{ 84 | \code{st_sftime}: An object of class \code{sftime}. 85 | 86 | Returned objects for subsetting functions: \code{[.sf} will return a 87 | \code{data.frame} or vector if the geometry column (of class \code{sfc}) is 88 | dropped (\code{drop=TRUE}), an \code{sfc} object if only the geometry column 89 | is selected, and otherwise return an \code{sftime} object. 90 | } 91 | \description{ 92 | Construct an \code{sftime} object from all its components 93 | } 94 | \details{ 95 | See also \link{[.data.frame}; for \code{[.sftime} \code{...} 96 | arguments are passed to \code{op}. 97 | } 98 | \examples{ 99 | ## construction with an sfc object 100 | library(sf) 101 | g <- st_sfc(st_point(1:2)) 102 | tc <- Sys.time() 103 | st_sftime(a = 3, g, time = tc) 104 | 105 | ## construction with an sf object 106 | \dontrun{ 107 | st_sftime(st_sf(a = 3, g), time = tc) 108 | # error, because if ... contains a data.frame-like object, no other objects 109 | # may be passed through ... . Instead, add the time column before. 110 | } 111 | 112 | st_sftime(st_sf(a = 3, g, time = tc)) 113 | 114 | ## Subsetting 115 | g <- st_sfc(st_point(c(1, 2)), st_point(c(1, 3)), st_point(c(2, 3)), 116 | st_point(c(2, 1)), st_point(c(3, 1))) 117 | tc <- Sys.time() + 1:5 118 | x <- st_sftime(a = 1:5, g, time = tc) 119 | 120 | # rows 121 | x[1, ] 122 | class(x[1, ]) 123 | 124 | x[x$a < 3, ] 125 | class(x[x$a < 3, ]) 126 | 127 | # columns 128 | x[, 1] 129 | class(x[, 1]) # drops time column as for ordinary data.frame subsetting, 130 | # keeps geometry column of sf object 131 | 132 | x[, 3] 133 | class(x[, 3]) # keeps time column because it is explicitly selected, 134 | # keeps geometry column of sf object, returns an sftime object 135 | 136 | x[, 3, drop = TRUE] 137 | class(x[, 3, drop = TRUE]) # if the geometry column is dropped, not only the 138 | # sf class is dropped, but also the sftime class 139 | 140 | x["a"] 141 | class(x["a"]) # Time columns are not sticky: If a column is selected by a 142 | # character vector and this does not contain the active time column, the time 143 | # column is dropped. 144 | 145 | x[c("a", "time")] 146 | class(x[c("a", "time")]) # keeps the time column 147 | 148 | # with sf or sftime object 149 | pol = st_sfc(st_polygon(list(cbind(c(0,2,2,0,0),c(0,0,2,2,0))))) 150 | h = st_sf(r = 5, pol) 151 | 152 | x[h, ] 153 | class(x[h, ]) # returns sftime object 154 | 155 | h[x, ] 156 | class(h[x, ]) # returns sf object 157 | 158 | ## Assigning values to columns 159 | 160 | # assigning new values to a non-time column 161 | x[["a"]] <- 5:1 162 | class(x) 163 | 164 | # assigning allowed new values to the time column 165 | x[["time"]] <- Sys.time() + 1:5 166 | class(x) 167 | 168 | # assigning new values to the time column which invalidate the time column 169 | x[["time"]] <- list(letters[1:2]) 170 | class(x) 171 | 172 | # assigning new values with `$` 173 | x$time <- Sys.time() + 1:5 174 | class(x) 175 | 176 | } 177 | -------------------------------------------------------------------------------- /man/st_time.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/st_time.R 3 | \name{st_time} 4 | \alias{st_time} 5 | \alias{st_time<-} 6 | \alias{st_time.sftime} 7 | \alias{st_time<-.sf} 8 | \alias{st_time<-.sftime} 9 | \alias{st_set_time} 10 | \alias{st_drop_time} 11 | \title{Get, set, or replace time information} 12 | \usage{ 13 | st_time(obj, ...) 14 | 15 | st_time(x, ...) <- value 16 | 17 | \method{st_time}{sftime}(obj, ...) 18 | 19 | \method{st_time}{sf}(x, ..., time_column_name = "time") <- value 20 | 21 | \method{st_time}{sftime}(x, ...) <- value 22 | 23 | st_set_time(x, value, ...) 24 | 25 | st_drop_time(x) 26 | } 27 | \arguments{ 28 | \item{obj}{An object of class \code{sftime}.} 29 | 30 | \item{...}{Additional arguments; Ignored.} 31 | 32 | \item{x}{An object of class \code{sftime} or \code{sf}.} 33 | 34 | \item{value}{An object for which \code{\link{is_sortable}} returns 35 | \code{TRUE} or an object of class \code{character}, or \code{NULL}.} 36 | 37 | \item{time_column_name}{Character value; The name of the column to set as 38 | active time column in \code{x}.} 39 | } 40 | \value{ 41 | \code{st_time} returns the content of the active time column of an 42 | \code{sftime} object. 43 | Assigning an object for which \code{\link{is_sortable}} returns \code{TRUE} 44 | to an \code{sf} object creates an \code{\link[=st_sftime]{sftime}} object. 45 | Assigning an object for which \code{\link{is_sortable}} returns \code{TRUE} 46 | to an \code{sftime} object replaces the active time column by this object. 47 | } 48 | \description{ 49 | Get, set, or replace time information 50 | } 51 | \details{ 52 | In case \code{value} is character and \code{x} is of class 53 | \code{sftime}, the active time column (as indicated by attribute 54 | \code{time_column}) is set to \code{x[[value]]}. 55 | 56 | The replacement function applied to \code{sftime} objects will overwrite the 57 | active time column, if \code{value} is \code{NULL}, it will remove it and 58 | coerce \code{x} to an \code{sftime} object. 59 | 60 | \code{st_drop_time} drops the time column of its argument, and 61 | reclasses it accordingly. 62 | } 63 | \examples{ 64 | # from sftime object 65 | g <- st_sfc(st_point(1:2)) 66 | time <- Sys.time() 67 | x <- st_sftime(a = 3, g, time = time) 68 | st_time(x) 69 | 70 | ## assign a vector with time information 71 | 72 | # to sf object 73 | x <- st_sf(a = 3, g) 74 | st_time(x) <- time 75 | x 76 | 77 | # to sftime object 78 | x <- st_sftime(a = 3, g, time = time) 79 | st_time(x) <- Sys.time() 80 | 81 | ## change the time column to another already existing column 82 | st_time(x) <- "a" 83 | 84 | ## remove time column from sftime object 85 | st_time(x) <- NULL 86 | 87 | ## pipe-friendly 88 | 89 | # assign time column to sf object 90 | x <- st_sf(a = 3, g) 91 | x <- st_set_time(x, time) 92 | 93 | # remove time column from sftime object 94 | st_set_time(x, NULL) 95 | 96 | ## drop time column and class 97 | 98 | # same as x <- st_set_time(x, NULL) 99 | st_drop_time(x) 100 | 101 | } 102 | -------------------------------------------------------------------------------- /man/tidyverse.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/join.R, R/tidyverse.R 3 | \name{tidyverse} 4 | \alias{tidyverse} 5 | \alias{inner_join.sftime} 6 | \alias{left_join.sftime} 7 | \alias{right_join.sftime} 8 | \alias{full_join.sftime} 9 | \alias{semi_join.sftime} 10 | \alias{anti_join.sftime} 11 | \alias{filter.sftime} 12 | \alias{arrange.sftime} 13 | \alias{group_by.sftime} 14 | \alias{ungroup.sftime} 15 | \alias{rowwise.sftime} 16 | \alias{mutate.sftime} 17 | \alias{transmute.sftime} 18 | \alias{select.sftime} 19 | \alias{rename.sftime} 20 | \alias{slice.sftime} 21 | \alias{summarise.sftime} 22 | \alias{summarize.sftime} 23 | \alias{distinct.sftime} 24 | \alias{gather.sftime} 25 | \alias{pivot_longer.sftime} 26 | \alias{spread.sftime} 27 | \alias{sample_n.sftime} 28 | \alias{sample_frac.sftime} 29 | \alias{nest.sftime} 30 | \alias{unnest.sftime} 31 | \alias{separate.sftime} 32 | \alias{unite.sftime} 33 | \alias{separate_rows.sftime} 34 | \alias{drop_na.sftime} 35 | \title{'tidyverse' methods for \code{sftime} objects} 36 | \usage{ 37 | inner_join.sftime(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...) 38 | 39 | left_join.sftime(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...) 40 | 41 | right_join.sftime(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...) 42 | 43 | full_join.sftime(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...) 44 | 45 | semi_join.sftime(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...) 46 | 47 | anti_join.sftime(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...) 48 | 49 | filter.sftime(.data, ..., .dots) 50 | 51 | arrange.sftime(.data, ..., .dots) 52 | 53 | group_by.sftime(.data, ..., add = FALSE) 54 | 55 | ungroup.sftime(.data, ...) 56 | 57 | rowwise.sftime(.data, ...) 58 | 59 | mutate.sftime(.data, ..., .dots) 60 | 61 | transmute.sftime(.data, ..., .dots) 62 | 63 | select.sftime(.data, ...) 64 | 65 | rename.sftime(.data, ...) 66 | 67 | slice.sftime(.data, ..., .dots) 68 | 69 | summarise.sftime(.data, ..., .dots, do_union = TRUE, is_coverage = FALSE) 70 | 71 | summarize.sftime(.data, ..., .dots, do_union = TRUE, is_coverage = FALSE) 72 | 73 | distinct.sftime(.data, ..., .keep_all = FALSE) 74 | 75 | gather.sftime( 76 | data, 77 | key, 78 | value, 79 | ..., 80 | na.rm = FALSE, 81 | convert = FALSE, 82 | factor_key = FALSE 83 | ) 84 | 85 | pivot_longer.sftime( 86 | data, 87 | cols, 88 | names_to = "name", 89 | names_prefix = NULL, 90 | names_sep = NULL, 91 | names_pattern = NULL, 92 | names_ptypes = NULL, 93 | names_transform = NULL, 94 | names_repair = "check_unique", 95 | values_to = "value", 96 | values_drop_na = FALSE, 97 | values_ptypes = NULL, 98 | values_transform = NULL, 99 | ... 100 | ) 101 | 102 | spread.sftime( 103 | data, 104 | key, 105 | value, 106 | fill = NA, 107 | convert = FALSE, 108 | drop = TRUE, 109 | sep = NULL 110 | ) 111 | 112 | sample_n.sftime( 113 | tbl, 114 | size, 115 | replace = FALSE, 116 | weight = NULL, 117 | .env = parent.frame() 118 | ) 119 | 120 | sample_frac.sftime( 121 | tbl, 122 | size = 1, 123 | replace = FALSE, 124 | weight = NULL, 125 | .env = parent.frame() 126 | ) 127 | 128 | nest.sftime(.data, ...) 129 | 130 | unnest.sftime(data, ..., .preserve = NULL) 131 | 132 | separate.sftime( 133 | data, 134 | col, 135 | into, 136 | sep = "[^[:alnum:]]+", 137 | remove = TRUE, 138 | convert = FALSE, 139 | extra = "warn", 140 | fill = "warn", 141 | ... 142 | ) 143 | 144 | unite.sftime(data, col, ..., sep = "_", remove = TRUE) 145 | 146 | separate_rows.sftime(data, ..., sep = "[^[:alnum:]]+", convert = FALSE) 147 | 148 | drop_na.sftime(data, ...) 149 | } 150 | \arguments{ 151 | \item{x}{An object of class \code{sftime}.} 152 | 153 | \item{y}{See \code{dplyr::`mutate-joins`}.} 154 | 155 | \item{by}{A join specification created with \code{\link[dplyr:join_by]{join_by()}}, or a character 156 | vector of variables to join by. 157 | 158 | If \code{NULL}, the default, \verb{*_join()} will perform a natural join, using all 159 | variables in common across \code{x} and \code{y}. A message lists the variables so 160 | that you can check they're correct; suppress the message by supplying \code{by} 161 | explicitly. 162 | 163 | To join on different variables between \code{x} and \code{y}, use a \code{\link[dplyr:join_by]{join_by()}} 164 | specification. For example, \code{join_by(a == b)} will match \code{x$a} to \code{y$b}. 165 | 166 | To join by multiple variables, use a \code{\link[dplyr:join_by]{join_by()}} specification with 167 | multiple expressions. For example, \code{join_by(a == b, c == d)} will match 168 | \code{x$a} to \code{y$b} and \code{x$c} to \code{y$d}. If the column names are the same between 169 | \code{x} and \code{y}, you can shorten this by listing only the variable names, like 170 | \code{join_by(a, c)}. 171 | 172 | \code{\link[dplyr:join_by]{join_by()}} can also be used to perform inequality, rolling, and overlap 173 | joins. See the documentation at \link[dplyr:join_by]{?join_by} for details on 174 | these types of joins. 175 | 176 | For simple equality joins, you can alternatively specify a character vector 177 | of variable names to join by. For example, \code{by = c("a", "b")} joins \code{x$a} 178 | to \code{y$a} and \code{x$b} to \code{y$b}. If variable names differ between \code{x} and \code{y}, 179 | use a named character vector like \code{by = c("x_a" = "y_a", "x_b" = "y_b")}. 180 | 181 | To perform a cross-join, generating all combinations of \code{x} and \code{y}, see 182 | \code{\link[dplyr:cross_join]{cross_join()}}.} 183 | 184 | \item{copy}{If \code{x} and \code{y} are not from the same data source, 185 | and \code{copy} is \code{TRUE}, then \code{y} will be copied into the 186 | same src as \code{x}. This allows you to join tables across srcs, but 187 | it is a potentially expensive operation so you must opt into it.} 188 | 189 | \item{suffix}{If there are non-joined duplicate variables in \code{x} and 190 | \code{y}, these suffixes will be added to the output to disambiguate them. 191 | Should be a character vector of length 2.} 192 | 193 | \item{...}{other arguments} 194 | 195 | \item{.data}{An object of class \code{stime}.} 196 | 197 | \item{.dots}{see corresponding function in package \code{dplyr}} 198 | 199 | \item{add}{see corresponding function in dplyr} 200 | 201 | \item{do_union}{logical; in case \code{summary} does not create a geometry column, should geometries be created by unioning using \link[sf]{st_union}, or simply by combining using \link[sf]{st_combine}? Using \link[sf]{st_union} resolves internal boundaries, but in case of unioning points, this will likely change the order of the points; see Details.} 202 | 203 | \item{is_coverage}{logical; if \code{do_union} is \code{TRUE}, use an optimized algorithm for features that form a polygonal coverage (have no overlaps)} 204 | 205 | \item{.keep_all}{see corresponding function in dplyr} 206 | 207 | \item{data}{see original function docs} 208 | 209 | \item{key}{see original function docs} 210 | 211 | \item{value}{see original function docs} 212 | 213 | \item{na.rm}{see original function docs} 214 | 215 | \item{convert}{see \link[tidyr]{separate_rows}} 216 | 217 | \item{factor_key}{see original function docs} 218 | 219 | \item{cols}{see original function docs} 220 | 221 | \item{names_to}{see original function docs} 222 | 223 | \item{names_prefix}{see original function docs} 224 | 225 | \item{names_sep}{see original function docs} 226 | 227 | \item{names_pattern}{see original function docs} 228 | 229 | \item{names_ptypes}{see original function docs} 230 | 231 | \item{names_transform}{see original function docs} 232 | 233 | \item{names_repair}{see original function docs} 234 | 235 | \item{values_to}{see original function docs} 236 | 237 | \item{values_drop_na}{see original function docs} 238 | 239 | \item{values_ptypes}{see original function docs} 240 | 241 | \item{values_transform}{see original function docs} 242 | 243 | \item{fill}{see original function docs} 244 | 245 | \item{drop}{see original function docs} 246 | 247 | \item{sep}{see \link[tidyr]{separate_rows}} 248 | 249 | \item{tbl}{see original function docs} 250 | 251 | \item{size}{see original function docs} 252 | 253 | \item{replace}{see original function docs} 254 | 255 | \item{weight}{see original function docs} 256 | 257 | \item{.env}{see original function docs} 258 | 259 | \item{.preserve}{see \link[tidyr:nest]{unnest}} 260 | 261 | \item{col}{see \link[tidyr]{separate}} 262 | 263 | \item{into}{see \link[tidyr]{separate}} 264 | 265 | \item{remove}{see \link[tidyr]{separate}} 266 | 267 | \item{extra}{see \link[tidyr]{separate}} 268 | } 269 | \value{ 270 | \itemize{ 271 | \item For \code{_join} methods: An object of class \code{sftime} 272 | representing the joining result of \code{x} and \code{y}. See 273 | \code{\link[dplyr]{mutate-joins}}. 274 | \item For \code{filter}: See \code{\link[dplyr]{filter}}. 275 | \item For \code{arrange}: See \code{\link[dplyr]{arrange}}. 276 | \item For \code{group_by} and \code{ungroup}: A grouped \code{sftime} 277 | object. See \code{\link[dplyr]{arrange}}. 278 | \item For \code{rowwise}: An \code{sftime} object. See 279 | \code{\link[dplyr]{rowwise}}. 280 | \item For \code{mutate} and \code{transmute}: See 281 | \code{\link[dplyr]{mutate}}. 282 | \item For \code{select}: See \code{\link[dplyr]{select}}. If the active 283 | time column is not explicitly selected, a \code{sf} object is returned. 284 | \item For \code{rename}: See \code{\link[dplyr]{rename}}. 285 | \item For \code{slice}: See \code{\link[dplyr]{slice}}. 286 | \item For \code{summarize} and \code{summarise}: See 287 | \code{\link[dplyr]{summarise}}. 288 | \item For \code{distinct}: See \code{\link[dplyr]{distinct}}. 289 | \item For \code{gather}: See \code{\link[tidyr]{gather}}. 290 | } 291 | } 292 | \description{ 293 | 'tidyverse' methods for \code{sftime} objects. Geometries are sticky, use 294 | \code{\link{as.data.frame}} to let \code{dplyr}'s own methods drop them. Use 295 | these methods without the \code{.sftime} suffix and after loading the 296 | 'tidyverse' package with the generic (or after loading package 'tidyverse'). 297 | } 298 | \examples{ 299 | g1 <- st_sfc(st_point(1:2), st_point(c(5, 8)), st_point(c(2, 9))) 300 | x1 <- st_sftime(a = 1:3, geometry = g1, time = Sys.time()) 301 | 302 | g2 <- st_sfc(st_point(c(4, 6)), st_point(c(4, 6)), st_point(c(4, 6))) 303 | x2 <- st_sftime(a = 2:4, geometry = g2, time = Sys.time()) 304 | 305 | library(dplyr) 306 | 307 | ## inner_join 308 | inner_join(x1, as.data.frame(x2), by = "a") # note: the active time column is 309 | # time.x and the active geometry column geometry.x 310 | 311 | inner_join(x2, as.data.frame(x1), by = "a") 312 | 313 | ## left_join 314 | left_join(x1, as.data.frame(x2), by = "a") 315 | 316 | left_join(x2, as.data.frame(x1), by = "a") 317 | 318 | ## right_join 319 | right_join(x1, as.data.frame(x2), by = "a") 320 | 321 | right_join(x2, as.data.frame(x1), by = "a") 322 | 323 | ## full_join 324 | full_join(x1, as.data.frame(x2), by = "a") 325 | 326 | full_join(x2, as.data.frame(x1), by = "a") 327 | 328 | ## semi_join 329 | semi_join(x1, as.data.frame(x2), by = "a") 330 | 331 | semi_join(x2, as.data.frame(x1), by = "a") 332 | 333 | ## anti_join 334 | anti_join(x1, as.data.frame(x2), by = "a") 335 | 336 | anti_join(x2, as.data.frame(x1), by = "a") 337 | 338 | ## filter 339 | filter(x1, a <= 2) 340 | 341 | ## arrange 342 | arrange(x1, dplyr::desc(a)) 343 | 344 | ## group_by 345 | group_by(x1, time) 346 | 347 | ## ungroup 348 | ungroup(group_by(x1, time)) 349 | 350 | ## rowwise 351 | x1 \%>\% 352 | mutate(a1 = 5:7) \%>\% 353 | rowwise() \%>\% 354 | mutate(a2 = mean(a, a1)) 355 | 356 | ## mutate 357 | x1 \%>\% 358 | mutate(a1 = 5:7) 359 | 360 | ## transmute 361 | x1 \%>\% 362 | transmute(a1 = 5:7) 363 | 364 | ## select 365 | x1 \%>\% 366 | select(-time) \%>\% 367 | select(geometry) 368 | 369 | ## rename 370 | x1 \%>\% 371 | rename(a1 = a) 372 | 373 | ## slice 374 | x1 \%>\% 375 | slice(1:2) 376 | 377 | ## summarise 378 | x1 \%>\% 379 | summarise(time = mean(time)) 380 | 381 | x1 \%>\% 382 | summarize(time = mean(time)) 383 | 384 | ## distinct 385 | x1 \%>\% 386 | distinct(geometry) 387 | 388 | ## gather 389 | library(tidyr) 390 | x1 \%>\% 391 | mutate(a1 = 5:7) \%>\% 392 | gather(key = "variable", value = "value", a, a1) 393 | 394 | ## pivot_longer 395 | x1 \%>\% 396 | mutate(a1 = 5:7) \%>\% 397 | pivot_longer(cols = c("a", "a1"), names_to = "variable", values_to = "value") 398 | 399 | ## spread 400 | x1 \%>\% 401 | mutate(a1 = 5:7) \%>\% 402 | gather(key = "variable", value = "value", a, a1) \%>\% 403 | spread(key = "variable", value = "value") 404 | 405 | ## sample_n 406 | set.seed(234) 407 | x1 \%>\% 408 | sample_n(size = 10, replace = TRUE) 409 | 410 | ## sample_frac 411 | x1 \%>\% 412 | sample_frac(size = 10, replace = TRUE) \%>\% 413 | sample_frac(size = 0.1, replace = FALSE) 414 | 415 | ## nest 416 | x1 \%>\% 417 | nest(a1 = -time) 418 | 419 | ## unnest 420 | x1 \%>\% 421 | mutate(a1 = list(1, c(1, 2), 5)) \%>\% 422 | unnest(a1) 423 | 424 | ## separate 425 | x1 \%>\% 426 | mutate(x = c(NA, "a.b", "a.d")) \%>\% 427 | separate(x, c("A", "B")) 428 | 429 | ## unite 430 | x1 \%>\% 431 | mutate(x = c(NA, "a.b", "a.d")) \%>\% 432 | separate(x, c("A", "B")) \%>\% 433 | unite(x, c("A", "B")) 434 | 435 | ## separate_rows 436 | x1 \%>\% 437 | mutate(z = c("1", "2,3,4", "5,6")) \%>\% 438 | separate_rows(z, convert = TRUE) 439 | 440 | ## drop_na 441 | x1 \%>\% 442 | mutate(z = c(1, 2, NA)) \%>\% 443 | drop_na(z) 444 | 445 | x1 \%>\% 446 | mutate(z = c(1, NA, NA)) \%>\% 447 | drop_na(z) 448 | 449 | x1 \%>\% 450 | mutate(time = replace(time, 1, NA)) \%>\% 451 | drop_na(time) 452 | } 453 | -------------------------------------------------------------------------------- /man/transform.sftime.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sftime.R 3 | \name{transform.sftime} 4 | \alias{transform.sftime} 5 | \title{Transform method for \code{sftime} objects} 6 | \usage{ 7 | \method{transform}{sftime}(`_data`, ...) 8 | } 9 | \arguments{ 10 | \item{_data}{An object of class \code{\link[=st_sftime]{sftime}}.} 11 | 12 | \item{...}{Further arguments of the form new_variable=expression} 13 | } 14 | \value{ 15 | \code{_data} (an \code{sftime} object) with modified attribute values 16 | (columns). 17 | } 18 | \description{ 19 | Can be used to create or modify attribute variables; for transforming 20 | geometries see \code{\link[sf]{st_transform}}, and all other functions starting with 21 | \code{st_}. 22 | } 23 | \examples{ 24 | # create an sftime object 25 | g <- st_sfc(st_point(c(1, 2)), st_point(c(1, 3)), st_point(c(2, 3)), 26 | st_point(c(2, 1)), st_point(c(3, 1))) 27 | x <- 28 | data.frame(a = 1:5, g, time = Sys.time() + 1:5, stringsAsFactors = FALSE) 29 | x_sftime <- st_as_sftime(x) 30 | x_sftime 31 | 32 | # modify values in column a 33 | transform(x_sftime, a = rev(a)) 34 | 35 | } 36 | -------------------------------------------------------------------------------- /revdep/.gitignore: -------------------------------------------------------------------------------- 1 | checks 2 | library 3 | checks.noindex 4 | library.noindex 5 | cloud.noindex 6 | data.sqlite 7 | *.html 8 | -------------------------------------------------------------------------------- /revdep/README.md: -------------------------------------------------------------------------------- 1 | # Platform 2 | 3 | |field |value | 4 | |:--------|:----------------------------------------| 5 | |version |R version 4.3.1 (2023-06-16 ucrt) | 6 | |os |Windows 11 x64 (build 22631) | 7 | |system |x86_64, mingw32 | 8 | |ui |RStudio | 9 | |language |(EN) | 10 | |collate |German_Germany.utf8 | 11 | |ctype |German_Germany.utf8 | 12 | |tz |Europe/Berlin | 13 | |date |2024-09-11 | 14 | |rstudio |2024.04.1+748 Chocolate Cosmos (desktop) | 15 | |pandoc |NA | 16 | 17 | # Dependencies 18 | 19 | |package |old |new |Δ | 20 | |:--------|:------|:------|:--| 21 | |sftime |0.2-0 |0.3.0 |* | 22 | |classInt |0.4-10 |0.4-10 | | 23 | |DBI |1.2.3 |1.2.3 | | 24 | |e1071 |1.7-14 |1.7-14 | | 25 | |magrittr |2.0.3 |2.0.3 | | 26 | |proxy |0.4-27 |0.4-27 | | 27 | |Rcpp |1.0.13 |1.0.13 | | 28 | |s2 |1.1.7 |1.1.7 | | 29 | |sf |1.0-17 |1.0-17 | | 30 | |units |0.8-5 |0.8-5 | | 31 | |wk |0.9.3 |0.9.3 | | 32 | 33 | # Revdeps 34 | 35 | ## Failed to check (1) 36 | 37 | |package |version |error |warning |note | 38 | |:-------|:-------|:-----|:-------|:----| 39 | |cubble |? | | | | 40 | 41 | -------------------------------------------------------------------------------- /revdep/cran.md: -------------------------------------------------------------------------------- 1 | ## revdepcheck results 2 | 3 | We checked 6 reverse dependencies (5 from CRAN + 1 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package. 4 | 5 | * We saw 0 new problems 6 | * We failed to check 0 packages 7 | 8 | -------------------------------------------------------------------------------- /revdep/failures.md: -------------------------------------------------------------------------------- 1 | # cubble 2 | 3 |
4 | 5 | * Version: 6 | * GitHub: https://github.com/r-spatial/sftime 7 | * Source code: NA 8 | * Number of recursive dependencies: 0 9 | 10 |
11 | 12 | ## Error before installation 13 | 14 | ### Devel 15 | 16 | ``` 17 | package 'abind' successfully unpacked and MD5 sums checked 18 | package 'anytime' successfully unpacked and MD5 sums checked 19 | package 'askpass' successfully unpacked and MD5 sums checked 20 | package 'backports' successfully unpacked and MD5 sums checked 21 | package 'base64enc' successfully unpacked and MD5 sums checked 22 | package 'bit' successfully unpacked and MD5 sums checked 23 | package 'bit64' successfully unpacked and MD5 sums checked 24 | package 'brio' successfully unpacked and MD5 sums checked 25 | package 'broom' successfully unpacked and MD5 sums checked 26 | package 'broom.helpers' successfully unpacked and MD5 sums checked 27 | ... 28 | package 'vctrs' successfully unpacked and MD5 sums checked 29 | package 'vdiffr' successfully unpacked and MD5 sums checked 30 | package 'viridisLite' successfully unpacked and MD5 sums checked 31 | package 'vroom' successfully unpacked and MD5 sums checked 32 | package 'waldo' successfully unpacked and MD5 sums checked 33 | package 'withr' successfully unpacked and MD5 sums checked 34 | package 'wk' successfully unpacked and MD5 sums checked 35 | package 'xfun' successfully unpacked and MD5 sums checked 36 | package 'xml2' successfully unpacked and MD5 sums checked 37 | package 'yaml' successfully unpacked and MD5 sums checked 38 | 39 | 40 | Error in download.file(url, destfile, method, mode = "wb", ...) : 41 | download from 'https://cran.rstudio.com/bin/windows/contrib/4.3/BH_1.84.0-0.zip' failed 42 | In addition: Warning messages: 43 | 1: In download.file(url, destfile, method, mode = "wb", ...) : 44 | downloaded length 0 != reported length 0 45 | 2: In download.file(url, destfile, method, mode = "wb", ...) : 46 | downloaded length 0 != reported length 0 47 | 3: In download.file(url, destfile, method, mode = "wb", ...) : 48 | URL 'https://cran.rstudio.com/bin/windows/contrib/4.3/BH_1.84.0-0.zip': Timeout of 60 seconds was reached 49 | 4: In download.file(url, destfile, method, mode = "wb", ...) : 50 | ... 51 | 2: In download.file(url, destfile, method, mode = "wb", ...) : 52 | downloaded length 0 != reported length 0 53 | 3: In download.file(url, destfile, method, mode = "wb", ...) : 54 | URL 'https://cran.rstudio.com/bin/windows/contrib/4.3/terra_1.7-78.zip': Timeout of 60 seconds was reached 55 | 4: In download.file(url, destfile, method, mode = "wb", ...) : 56 | URL 'https://cran.rstudio.com/bin/windows/contrib/4.3/terra_1.7-78.zip': Timeout of 60 seconds was reached 57 | Warning in download.packages(pkgs, destdir = tmpd, available = available, : 58 | download of package 'terra' failed 59 | Warning in download.packages(pkgs, destdir = tmpd, available = available, : 60 | download of package 'terra' failed 61 | 62 | 63 | ``` 64 | ### CRAN 65 | 66 | ``` 67 | package 'abind' successfully unpacked and MD5 sums checked 68 | package 'anytime' successfully unpacked and MD5 sums checked 69 | package 'askpass' successfully unpacked and MD5 sums checked 70 | package 'backports' successfully unpacked and MD5 sums checked 71 | package 'base64enc' successfully unpacked and MD5 sums checked 72 | package 'bit' successfully unpacked and MD5 sums checked 73 | package 'bit64' successfully unpacked and MD5 sums checked 74 | package 'brio' successfully unpacked and MD5 sums checked 75 | package 'broom' successfully unpacked and MD5 sums checked 76 | package 'broom.helpers' successfully unpacked and MD5 sums checked 77 | ... 78 | package 'vctrs' successfully unpacked and MD5 sums checked 79 | package 'vdiffr' successfully unpacked and MD5 sums checked 80 | package 'viridisLite' successfully unpacked and MD5 sums checked 81 | package 'vroom' successfully unpacked and MD5 sums checked 82 | package 'waldo' successfully unpacked and MD5 sums checked 83 | package 'withr' successfully unpacked and MD5 sums checked 84 | package 'wk' successfully unpacked and MD5 sums checked 85 | package 'xfun' successfully unpacked and MD5 sums checked 86 | package 'xml2' successfully unpacked and MD5 sums checked 87 | package 'yaml' successfully unpacked and MD5 sums checked 88 | 89 | 90 | Error in download.file(url, destfile, method, mode = "wb", ...) : 91 | download from 'https://cran.rstudio.com/bin/windows/contrib/4.3/BH_1.84.0-0.zip' failed 92 | In addition: Warning messages: 93 | 1: In download.file(url, destfile, method, mode = "wb", ...) : 94 | downloaded length 0 != reported length 0 95 | 2: In download.file(url, destfile, method, mode = "wb", ...) : 96 | downloaded length 0 != reported length 0 97 | 3: In download.file(url, destfile, method, mode = "wb", ...) : 98 | URL 'https://cran.rstudio.com/bin/windows/contrib/4.3/BH_1.84.0-0.zip': Timeout of 60 seconds was reached 99 | 4: In download.file(url, destfile, method, mode = "wb", ...) : 100 | ... 101 | 2: In download.file(url, destfile, method, mode = "wb", ...) : 102 | downloaded length 0 != reported length 0 103 | 3: In download.file(url, destfile, method, mode = "wb", ...) : 104 | URL 'https://cran.rstudio.com/bin/windows/contrib/4.3/terra_1.7-78.zip': Timeout of 60 seconds was reached 105 | 4: In download.file(url, destfile, method, mode = "wb", ...) : 106 | URL 'https://cran.rstudio.com/bin/windows/contrib/4.3/terra_1.7-78.zip': Timeout of 60 seconds was reached 107 | Warning in download.packages(pkgs, destdir = tmpd, available = available, : 108 | download of package 'terra' failed 109 | Warning in download.packages(pkgs, destdir = tmpd, available = available, : 110 | download of package 'terra' failed 111 | 112 | 113 | ``` 114 | -------------------------------------------------------------------------------- /revdep/problems.md: -------------------------------------------------------------------------------- 1 | *Wow, no problems at all. :)* -------------------------------------------------------------------------------- /tic.R: -------------------------------------------------------------------------------- 1 | do_package_checks() 2 | 3 | if (ci_on_travis()) { 4 | do_pkgdown() 5 | } 6 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /vignettes/sftime.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Introduction to sftime" 3 | author: "Henning Teickner, Benedikt Gräler, Edzer Pebesma" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Introduction to sftime} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | ```{r setup, include = FALSE} 13 | knitr::opts_chunk$set( 14 | collapse = TRUE, 15 | comment = "#>" 16 | ) 17 | ``` 18 | 19 | The package `sftime` extends package `sf` to store and handle spatiotemporal data. To this end, `sftime` introduces a dedicated time column that stores the temporal information alongside the simple features column of an `sf` object. 20 | 21 | The time column can consists of any collection of a class that allows to be sorted - reflecting the native order of time. Besides well-known time classes such as `Date` or `POSIXct`, it also allows for custom class definitions that come with the necessary methods to make sorting work (we will see a example below). 22 | 23 | This vignette briefly explains and illustrates the ideas and decisions behind the implementation of `sftime`. 24 | 25 | ```{r packages} 26 | # load required packages 27 | library(sftime) 28 | library(sf) 29 | library(stars) 30 | library(spacetime) 31 | library(ggplot2) 32 | library(tidyr) 33 | library(magrittr) 34 | ``` 35 | 36 | 37 | ## The `sftime` class 38 | 39 | An `sftime` object is an `sf` object with an additional time column that contains the temporal information alongside the simple features column. This allows it to handle irregular and regular temporal information. 40 | 41 | For spatiotemporal data with regular temporal data (raster or vector data cubes: data where each geometry is observed at the same set of time instances), package `stars` is developed as a powerful alternative (e.g. time series of remote sensing imagery, regular measurements of entire measurement network). `sftime` fills the gap for data where arbitrary combinations of geometry and time occur, including irregularly collected sensor data or (spatiotemporal) point pattern data. 42 | 43 | `sftime` objects can be constructed directly from `sfc` objects by combining them with a vector representing temporal information: 44 | 45 | ```{r sftime-class-1} 46 | # example sfc object 47 | x_sfc <- 48 | sf::st_sfc( 49 | sf::st_point(1:2), 50 | sf::st_point(c(1,3)), 51 | sf::st_point(2:3), 52 | sf::st_point(c(2,1)) 53 | ) 54 | 55 | # create an sftime object directly from x_sfc 56 | x_sftime1 <- sftime::st_sftime(a = 1:4, x_sfc, time = Sys.time()- 0:3 * 3600 * 24) 57 | 58 | # first create the sf object and from this the sftime object 59 | x_sf <- sf::st_sf(a = 1:4, x_sfc, time = x_sftime1$time) 60 | x_sftime2 <- sftime::st_sftime(x_sf) 61 | 62 | x_sftime3 <- sftime::st_as_sftime(x_sf) # alernative option 63 | 64 | identical(x_sftime1, x_sftime2) 65 | identical(x_sftime1, x_sftime3) 66 | 67 | x_sftime1 68 | ``` 69 | 70 | Methods for `sftime` objects are: 71 | 72 | ```{r sftime-class-2} 73 | methods(class = "sftime") 74 | ``` 75 | 76 | Methods for `sf` objects which are not listed above work also for `sftime` objects. 77 | 78 | 79 | ## Functions to get or set the time column of an `sftime` object 80 | 81 | Functions to get or set the time column of an `sftime` object are: 82 | 83 | ```{r time-column-1} 84 | # get the values from the time column 85 | st_time(x_sftime1) 86 | x_sftime1$time # alternative way 87 | 88 | # set the values in the time column 89 | st_time(x_sftime1) <- Sys.time() 90 | st_time(x_sftime1) 91 | 92 | # drop the time column to convert an sftime object to an sf object 93 | st_drop_time(x_sftime1) 94 | x_sftime1 95 | 96 | # add a time column to an sf object converts it to an sftime object 97 | st_time(x_sftime1, time_column_name = "time") <- Sys.time() 98 | class(x_sftime1) 99 | 100 | # These can also be used with pipes 101 | x_sftime1 <- 102 | x_sftime1 %>% 103 | st_drop_time() %>% 104 | st_set_time(Sys.time(), time_column_name = "time") 105 | ``` 106 | 107 | 108 | ## Conversion to class `sftime` 109 | 110 | sftime supports coercion to `sftime` objects from the following classes (grouped according to packages): 111 | 112 | - sf: sf 113 | - stars: stars 114 | - spacetime: STI, STIDF 115 | - trajectories: Track, Tracks, TracksCollection 116 | - sftrack: sftrack, sftraj 117 | - cubble: cubble_df 118 | 119 | **Conversion from `sf` objects:** 120 | 121 | ```{r} 122 | # define the geometry column 123 | g <- 124 | st_sfc( 125 | st_point(c(1, 2)), 126 | st_point(c(1, 3)), 127 | st_point(c(2, 3)), 128 | st_point(c(2, 1)), 129 | st_point(c(3, 1)) 130 | ) 131 | 132 | # crate sf object 133 | x4_sf <- st_sf(a = 1:5, g, time = Sys.time() + 1:5) 134 | 135 | # convert to sftime 136 | x4_sftime <- st_as_sftime(x4_sf) 137 | class(x4_sftime) 138 | ``` 139 | 140 | **Conversion from `stars` objects:** 141 | 142 | ```{r} 143 | # load sample data 144 | x5_stars <- stars::read_ncdf(system.file("nc/bcsd_obs_1999.nc", package = "stars"), var = c("pr", "tas")) 145 | 146 | # convert to sftime 147 | x5_sftime <- st_as_sftime(x5_stars, time_column_name = "time") 148 | ``` 149 | 150 | `st_as_sftime.stars` is a wrapper around `st_as_sf.stars`. As a consequence, some dimensions of the `stars` object can be dropped during conversion. Temporal information in `stars` objects are typically stored as dimension of an attribute. Therefore, some argument settings to `st_as_sftime` can drop the dimension with temporal information and therefore throw an error. For example, setting `merge = TRUE` drops dimension `time` and therefore conversion fails. Similarly, setting `long = FALSE` returns the attribute values in a wide format, where each column is a time point: 151 | 152 | ```{r, error = TRUE} 153 | # failed conversion to sftime 154 | x5_sftime <- st_as_sftime(x5_stars, merge = TRUE, time_column_name = "time") 155 | x5_sftime <- st_as_sftime(x5_stars, long = FALSE, time_column_name = "time") 156 | ``` 157 | 158 | **Conversion from `spacetime` objects** 159 | 160 | ```{r} 161 | # get sample data 162 | example(STI, package = "spacetime") 163 | class(stidf) 164 | 165 | # conversion to sftime 166 | x1_sftime <- st_as_sftime(stidf) 167 | ``` 168 | 169 | **Conversion from `Track`, `Tracks`, `TracksCollections` objects (trajectories package)** 170 | 171 | ```{r} 172 | # get a sample TracksCollection 173 | x2_TracksCollection <- trajectories::rTracksCollection(p = 2, m = 3, n = 40) 174 | 175 | # convert to sftime 176 | x2_TracksCollection_sftime <- st_as_sftime(x2_TracksCollection) 177 | x2_Tracks_sftime <- st_as_sftime(x2_TracksCollection@tracksCollection[[1]]) 178 | x2_Track_sftime <- st_as_sftime(x2_TracksCollection@tracksCollection[[1]]@tracks[[1]]) 179 | ``` 180 | 181 | **Conversion from `cubble_df` objects** 182 | 183 | Both, nested and long-form `cubble_df` can be converted to class `sftime`. If the `cubble_df` object has no simple features column (is not also of class `sf`), the function first converts longitude and latitude to a simple features column using `cubble::add_geometry_column()`. 184 | 185 | ```{r, eval=TRUE, echo=TRUE} 186 | # get a sample cubble_df object 187 | climate_aus <- cubble::climate_aus 188 | 189 | # convert to sftime 190 | climate_aus_sftime <- 191 | st_as_sftime(climate_aus[1:4, ]) 192 | 193 | climate_aus_sftime <- 194 | st_as_sftime(cubble::face_temporal(climate_aus)[1:4, ]) 195 | ``` 196 | 197 | 198 | ## Subsetting 199 | 200 | Different subsetting methods exist for `sftime` objects. Since `sftime` objects are built on top of `sf` objects, all subsetting methods for `sf` objects also work for `sftime` objects. 201 | 202 | Above (section [The `sftime` class]), the method to subset the time column was introduced: 203 | 204 | ```{r} 205 | st_time(x_sftime1) 206 | ``` 207 | 208 | Other subsetting functions work as for `sf` objects, e.g. selecting rows by row indices returns the specified rows. A key difference is that the active time column of an `sftime` object is not sticky --- in contrast to the active simple feature column in `sf` objects. 209 | Therefore, the active time column of an `sftime` object always has to be selected explicitly. If omitted, the subset will simplify to an `sf` objects without the active time column: 210 | 211 | ```{r} 212 | # selecting rows and columns (works just as for sf objects) 213 | x_sftime1[1, ] 214 | x_sftime1[, 3] 215 | 216 | # beware: the time column is not sticky. If omitted, the subset becomes an sf object 217 | class(x_sftime1[, 1]) 218 | class(x_sftime1["a"]) # the same 219 | x_sftime1[, 1] 220 | 221 | # to retain the time column and an sftime object, explicitly select the time column during subsetting: 222 | class(x_sftime1[, c(1, 3)]) 223 | class(x_sftime1[c("a", "time")]) # the same 224 | ``` 225 | 226 | 227 | ## Plotting 228 | 229 | For quick plotting, a plot method exists for `sftime` objects, which plots longitude-latitude coordinates and colors simple features according to values of a specified variable. Different panels are plotted for different time intervals which can be specified. Simple feature geometries might be overlaid several times when multiple observations fall in the same time interval. This is similar to `stplot()` from package spacetime with `mode = "xy"`: 230 | 231 | ```{r plotting-plot.sftime-1, fig.width=7} 232 | coords <- matrix(runif(100), ncol = 2) 233 | g <- sf::st_sfc(lapply(1:50, function(i) st_point(coords[i, ]) )) 234 | 235 | x_sftime4 <- 236 | st_sftime( 237 | a = 1:200, 238 | b = rnorm(200), 239 | id_object = as.factor(rep(1:4,each=50)), 240 | geometry = g, 241 | time = as.POSIXct("2020-09-01 00:00:00") + 0:49 * 3600 * 6 242 | ) 243 | 244 | plot(x_sftime4, key.pos = 4) 245 | ``` 246 | 247 | The plotting method internally uses the `plot` method for `sf` objects. This makes it possible to customize plot appearance using the arguments of `plot.sf()`, for example: 248 | 249 | ```{r plotting-plot.sftime-2, fig.width=7} 250 | plot(x_sftime4, number = 10, max.plot = 10, key.pos = 4) 251 | ``` 252 | 253 | To create customized plots or plots which have different variables on plot axes than longitude and latitude, we recommend using ggplot2. For example, the plot method output can be mimicked by: 254 | 255 | ```{r plotting-ggplot-1, fig.width=7} 256 | library(ggplot2) 257 | 258 | ggplot() + 259 | geom_sf(data = x_sftime4, aes(color = b)) + 260 | facet_wrap(~ cut_number(time, n = 6)) + 261 | theme( 262 | panel.spacing.x = unit(4, "mm"), 263 | panel.spacing.y = unit(4, "mm") 264 | ) 265 | ``` 266 | 267 | This strategy can also be used to create other plots, for example plotting the id of entities over time (similar to `stplot()` with `mode = "xt"`): 268 | 269 | ```{r plotting-ggplot-2, fig.width=7} 270 | ggplot(x_sftime4) + 271 | geom_point(aes(y = id_object, x = time, color = b)) 272 | ``` 273 | 274 | Or for plotting time series of values of all variables with different panels for each entity (location) defined via a categorical variable (similar to `stplot()` with `mode = "tp"`): 275 | 276 | ```{r plotting-ggplot-3, fig.width=7} 277 | x_sftime4 %>% 278 | tidyr::pivot_longer(cols = c("a", "b"), names_to = "variable", values_to = "value") %>% 279 | ggplot() + 280 | geom_path(aes(y = value, x = time, color = variable)) + 281 | facet_wrap(~ id_object) 282 | ``` 283 | 284 | Or for plotting time series of values of all variables for all entities defined via a categorical variable with different panels for each variable (similar to `stplot()` with `mode = "ts"`): 285 | 286 | ```{r plotting-ggplot-4, fig.width=7} 287 | x_sftime4 %>% 288 | tidyr::pivot_longer(cols = c("a", "b"), names_to = "variable", values_to = "value") %>% 289 | ggplot() + 290 | geom_path(aes(y = value, x = time, color = id_object)) + 291 | facet_wrap(~ variable, scales = "free_y") 292 | ``` 293 | 294 | 295 | ## User-defined time columns 296 | 297 | The time column is a special column of the underlying sf object which defines time information (timestamps and temporal ordering) alongside the simple features column of an sf object. Common time representations in R (e.g. `POSIXct`, `POSIXlt`, `Date`, `yearmon`, `yearqtr`) are allowed, as well as optional user-defined types. Let us look at a simple example where we define a time column based on `POSIXct` 298 | 299 | ```{r, eval=TRUE} 300 | (tc <- as.POSIXct("2020-09-01 08:00:00")-0:3*3600*24) 301 | ``` 302 | 303 | The ordering is not altered upon construction (as in some other representations). If a different order is required, the `order` function and `sort` method can be applied to the time column: 304 | 305 | ```{r} 306 | tc 307 | order(tc) 308 | sort(tc) 309 | ``` 310 | 311 | In some applications it might be useful to have more complex temporal information such as intervals of different length. The following example is also meant as template for other user-defined classes which could be used to build the time column of the sftime class. 312 | 313 | At first, we will need a few helper functions: 314 | 315 | ```{r} 316 | # utility functions 317 | as.character.interval <- function(x) { 318 | paste0("[", x[1], ", ", x[2], "]") 319 | } 320 | 321 | print.interval <- function(x, ...) { 322 | cat("Interval:", as.character(x), "\n") 323 | } 324 | 325 | #'[.intervals' <- function(x, i) { 326 | # sx <- unclass(x)[i] 327 | # class(sx) <- "intervals" 328 | # sx 329 | #} 330 | ``` 331 | 332 | Now, we can define the different intervals used to represent our temporal information: 333 | 334 | ```{r} 335 | # time interval definition 336 | i1 <- c(5.3,12) 337 | class(i1) <- "interval" 338 | i2 <- c(3.1,6) 339 | class(i2) <- "interval" 340 | i3 <- c(1.4,6.9) 341 | class(i3) <- "interval" 342 | i4 <- c(1,21) 343 | class(i4) <- "interval" 344 | 345 | intrvls <- structure(list(i1, i2, i3, i4), class = "Intervals") 346 | # provide dedicated generic to xtfrm for class intervals 347 | ``` 348 | 349 | The advantage is to be able to define different sorting approaches: 350 | 351 | ```{r} 352 | xtfrm.Intervals <- function(x) sapply(x, mean) 353 | # - sort by centre 354 | (tc <- intrvls) 355 | order(tc) 356 | sort(tc)[1] 357 | ``` 358 | 359 | ```{r} 360 | # - sort by end 361 | xtfrm.Intervals <- function(x) sapply(x, max) 362 | (tc <- intrvls) 363 | order(tc) 364 | sort(tc)[1] 365 | ``` 366 | 367 | ```{r} 368 | # - sort by start 369 | xtfrm.Intervals <- function(x) sapply(x, min) 370 | tc <- intrvls 371 | order(tc) 372 | sort(tc)[1] 373 | ``` 374 | 375 | Based on the sorting procedure (begin, centre or end of the interval), the smallest element (each last line) and the order of the time column changes. 376 | --------------------------------------------------------------------------------