├── _pkgdown.yml ├── LICENSE ├── data ├── build.rda ├── tmy.RData ├── tmy2.RData ├── boston_block.RData ├── boston_build.RData ├── boston_park.RData ├── beersheva_build.RData ├── beersheva_elev.RData └── boston_sidewalk.RData ├── tests ├── testthat.R └── testthat │ ├── test_shadowFootprint.R │ ├── test_SVF.R │ ├── test_shadowHeight.R │ └── test_radiation.R ├── README-demo1-1.png ├── README-demo1-2.png ├── .gitignore ├── README-abstract.png ├── docs ├── README-demo1-1.png ├── README-demo1-2.png ├── README-abstract.png ├── reference │ ├── SVF-1.png │ ├── build-1.png │ ├── ray-1.png │ ├── toSeg-1.png │ ├── Rplot001.png │ ├── Rplot002.png │ ├── Rplot003.png │ ├── shiftAz-1.png │ ├── classifyAz-1.png │ ├── coefDirect-1.png │ ├── coefDirect-2.png │ ├── inShadow-1.png │ ├── boston_block-1.png │ ├── boston_build-1.png │ ├── boston_park-1.png │ ├── shadowHeight-1.png │ ├── surfaceGrid-1.png │ ├── surfaceGrid-2.png │ ├── surfaceGrid-3.png │ ├── beersheva_build-1.png │ ├── beersheva_elev-1.png │ ├── boston_sidewalk-1.png │ ├── shadowFootprint-1.png │ ├── deg2rad.html │ ├── rad2deg.html │ ├── boston_block.html │ ├── boston_park.html │ ├── toGMT.html │ ├── boston_sidewalk.html │ ├── build.html │ ├── beersheva_elev.html │ └── boston_build.html ├── pkgdown.yml ├── link.svg ├── bootstrap-toc.css ├── docsearch.js ├── pkgdown.js ├── bootstrap-toc.js ├── LICENSE-text.html ├── 404.html └── authors.html ├── data-raw ├── input │ ├── build.RData │ ├── tmy.RData │ ├── tmy2.RData │ ├── boston_block.RData │ ├── boston_build.RData │ ├── boston_park.RData │ ├── beersheva_build.RData │ ├── beersheva_elev.RData │ └── boston_sidewalk.RData ├── vignette │ └── images │ │ ├── SVF2.pdf │ │ ├── shadow_height2b.pdf │ │ └── shadow_image_rishon.pdf ├── DATASET.R └── ethis::use_data(build)[201~ ├── vignettes ├── introduction2.pdf └── introduction2.pdf.asis ├── CRAN-RELEASE ├── .Rbuildignore ├── R ├── deg2rad.R ├── rad2deg.R ├── toGMT.R ├── sunLocation.R ├── ray.R ├── shadow-package.R ├── solarpos2.R ├── radiationGrid.R ├── classifyAz.R ├── shiftAz.R ├── toSeg.R ├── plotGrid.R ├── shadowHeightPnt.R ├── checkInputs.R ├── coefDirect.R ├── SVFPnt.R ├── data.R └── shadowFootprint.R ├── cran-comments.md ├── man ├── deg2rad.Rd ├── rad2deg.Rd ├── boston_park.Rd ├── toGMT.Rd ├── boston_sidewalk.Rd ├── boston_block.Rd ├── build.Rd ├── classifyAz.Rd ├── boston_build.Rd ├── beersheva_elev.Rd ├── shiftAz.Rd ├── beersheva_build.Rd ├── ray.Rd ├── toSeg.Rd ├── tmy.Rd ├── tmy2.Rd ├── solarpos2.Rd ├── shadow.Rd ├── plotGrid.Rd ├── coefDirect.Rd ├── surfaceGrid.Rd ├── shadowFootprint.Rd ├── SVF.Rd └── shadowHeight.Rd ├── NAMESPACE ├── DESCRIPTION ├── README.md ├── README.Rmd └── NEWS.md /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2020 2 | COPYRIGHT HOLDER: Michael Dorman 3 | -------------------------------------------------------------------------------- /data/build.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data/build.rda -------------------------------------------------------------------------------- /data/tmy.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data/tmy.RData -------------------------------------------------------------------------------- /data/tmy2.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data/tmy2.RData -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(shadow) 3 | 4 | test_check("shadow") 5 | -------------------------------------------------------------------------------- /README-demo1-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/README-demo1-1.png -------------------------------------------------------------------------------- /README-demo1-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/README-demo1-2.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Meta 2 | doc 3 | .Rproj.user 4 | .Rhistory 5 | .RData 6 | .Ruserdata 7 | inst/doc 8 | -------------------------------------------------------------------------------- /README-abstract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/README-abstract.png -------------------------------------------------------------------------------- /data/boston_block.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data/boston_block.RData -------------------------------------------------------------------------------- /data/boston_build.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data/boston_build.RData -------------------------------------------------------------------------------- /data/boston_park.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data/boston_park.RData -------------------------------------------------------------------------------- /docs/README-demo1-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/README-demo1-1.png -------------------------------------------------------------------------------- /docs/README-demo1-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/README-demo1-2.png -------------------------------------------------------------------------------- /data-raw/input/build.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data-raw/input/build.RData -------------------------------------------------------------------------------- /data-raw/input/tmy.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data-raw/input/tmy.RData -------------------------------------------------------------------------------- /data-raw/input/tmy2.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data-raw/input/tmy2.RData -------------------------------------------------------------------------------- /data/beersheva_build.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data/beersheva_build.RData -------------------------------------------------------------------------------- /data/beersheva_elev.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data/beersheva_elev.RData -------------------------------------------------------------------------------- /data/boston_sidewalk.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data/boston_sidewalk.RData -------------------------------------------------------------------------------- /docs/README-abstract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/README-abstract.png -------------------------------------------------------------------------------- /docs/reference/SVF-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/SVF-1.png -------------------------------------------------------------------------------- /docs/reference/build-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/build-1.png -------------------------------------------------------------------------------- /docs/reference/ray-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/ray-1.png -------------------------------------------------------------------------------- /docs/reference/toSeg-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/toSeg-1.png -------------------------------------------------------------------------------- /docs/reference/Rplot001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/Rplot001.png -------------------------------------------------------------------------------- /docs/reference/Rplot002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/Rplot002.png -------------------------------------------------------------------------------- /docs/reference/Rplot003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/Rplot003.png -------------------------------------------------------------------------------- /docs/reference/shiftAz-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/shiftAz-1.png -------------------------------------------------------------------------------- /vignettes/introduction2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/vignettes/introduction2.pdf -------------------------------------------------------------------------------- /docs/reference/classifyAz-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/classifyAz-1.png -------------------------------------------------------------------------------- /docs/reference/coefDirect-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/coefDirect-1.png -------------------------------------------------------------------------------- /docs/reference/coefDirect-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/coefDirect-2.png -------------------------------------------------------------------------------- /docs/reference/inShadow-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/inShadow-1.png -------------------------------------------------------------------------------- /data-raw/input/boston_block.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data-raw/input/boston_block.RData -------------------------------------------------------------------------------- /data-raw/input/boston_build.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data-raw/input/boston_build.RData -------------------------------------------------------------------------------- /data-raw/input/boston_park.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data-raw/input/boston_park.RData -------------------------------------------------------------------------------- /data-raw/vignette/images/SVF2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data-raw/vignette/images/SVF2.pdf -------------------------------------------------------------------------------- /docs/reference/boston_block-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/boston_block-1.png -------------------------------------------------------------------------------- /docs/reference/boston_build-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/boston_build-1.png -------------------------------------------------------------------------------- /docs/reference/boston_park-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/boston_park-1.png -------------------------------------------------------------------------------- /docs/reference/shadowHeight-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/shadowHeight-1.png -------------------------------------------------------------------------------- /docs/reference/surfaceGrid-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/surfaceGrid-1.png -------------------------------------------------------------------------------- /docs/reference/surfaceGrid-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/surfaceGrid-2.png -------------------------------------------------------------------------------- /docs/reference/surfaceGrid-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/surfaceGrid-3.png -------------------------------------------------------------------------------- /data-raw/input/beersheva_build.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data-raw/input/beersheva_build.RData -------------------------------------------------------------------------------- /data-raw/input/beersheva_elev.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data-raw/input/beersheva_elev.RData -------------------------------------------------------------------------------- /data-raw/input/boston_sidewalk.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data-raw/input/boston_sidewalk.RData -------------------------------------------------------------------------------- /docs/pkgdown.yml: -------------------------------------------------------------------------------- 1 | pandoc: '2.5' 2 | pkgdown: 1.6.1 3 | pkgdown_sha: ~ 4 | articles: {} 5 | last_built: 2020-10-09T19:59Z 6 | 7 | -------------------------------------------------------------------------------- /docs/reference/beersheva_build-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/beersheva_build-1.png -------------------------------------------------------------------------------- /docs/reference/beersheva_elev-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/beersheva_elev-1.png -------------------------------------------------------------------------------- /docs/reference/boston_sidewalk-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/boston_sidewalk-1.png -------------------------------------------------------------------------------- /docs/reference/shadowFootprint-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/docs/reference/shadowFootprint-1.png -------------------------------------------------------------------------------- /CRAN-RELEASE: -------------------------------------------------------------------------------- 1 | This package was submitted to CRAN on 2020-12-16. 2 | Once it is accepted, delete this file and tag the release (commit 72eb116). 3 | -------------------------------------------------------------------------------- /data-raw/vignette/images/shadow_height2b.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data-raw/vignette/images/shadow_height2b.pdf -------------------------------------------------------------------------------- /data-raw/vignette/images/shadow_image_rishon.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeldorman/shadow/HEAD/data-raw/vignette/images/shadow_image_rishon.pdf -------------------------------------------------------------------------------- /data-raw/DATASET.R: -------------------------------------------------------------------------------- 1 | library(sp) 2 | library(raster) 3 | 4 | # build 5 | load("input/build.RData") 6 | crs(build) = NA 7 | usethis::use_data(build, overwrite = TRUE) 8 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^CRAN-RELEASE$ 2 | ^Meta$ 3 | ^doc$ 4 | ^.*\.Rproj$ 5 | ^\.Rproj\.user$ 6 | ^README\.Rmd$ 7 | ^README-.*\.png$ 8 | cran-comments.md 9 | README.md 10 | README.Rmd 11 | ^_pkgdown\.yml$ 12 | ^docs$ 13 | ^pkgdown$ 14 | ^data-raw$ 15 | -------------------------------------------------------------------------------- /vignettes/introduction2.pdf.asis: -------------------------------------------------------------------------------- 1 | %\VignetteIndexEntry{R packages: Static PDF and HTML vignettes} 2 | %\VignetteEngine{R.rsp::asis} 3 | %\VignetteKeyword{PDF} 4 | %\VignetteKeyword{HTML} 5 | %\VignetteKeyword{vignette} 6 | %\VignetteKeyword{package} 7 | -------------------------------------------------------------------------------- /R/deg2rad.R: -------------------------------------------------------------------------------- 1 | #' Degrees to radians 2 | #' 3 | #' @param deg Angle in degrees 4 | #' 5 | #' @return \code{numeric} Angle in radians 6 | #' @export 7 | #' 8 | #' @examples 9 | #' deg2rad(360) == 2*pi 10 | 11 | deg2rad = function(deg) {(deg * pi) / (180)} 12 | -------------------------------------------------------------------------------- /R/rad2deg.R: -------------------------------------------------------------------------------- 1 | #' Radians to degrees 2 | #' 3 | #' @param rad Angle in radians 4 | #' 5 | #' @return \code{numeric} Angle in degrees 6 | #' @export 7 | #' 8 | #' @examples 9 | #' rad2deg(2*pi) == 360 10 | 11 | rad2deg = function(rad) {(rad * 180) / (pi)} 12 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## Test environments 2 | 3 | * local Ubuntu 20.04 install, R 4.0.3 4 | * win-builder (devel and release) 5 | 6 | ## R CMD check results 7 | 8 | There were no ERRORs or WARNINGs 9 | 10 | ## Downstream dependencies 11 | 12 | None. 13 | -------------------------------------------------------------------------------- /man/deg2rad.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/deg2rad.R 3 | \name{deg2rad} 4 | \alias{deg2rad} 5 | \title{Degrees to radians} 6 | \usage{ 7 | deg2rad(deg) 8 | } 9 | \arguments{ 10 | \item{deg}{Angle in degrees} 11 | } 12 | \value{ 13 | \code{numeric} Angle in radians 14 | } 15 | \description{ 16 | Degrees to radians 17 | } 18 | \examples{ 19 | deg2rad(360) == 2*pi 20 | } 21 | -------------------------------------------------------------------------------- /man/rad2deg.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rad2deg.R 3 | \name{rad2deg} 4 | \alias{rad2deg} 5 | \title{Radians to degrees} 6 | \usage{ 7 | rad2deg(rad) 8 | } 9 | \arguments{ 10 | \item{rad}{Angle in radians} 11 | } 12 | \value{ 13 | \code{numeric} Angle in degrees 14 | } 15 | \description{ 16 | Radians to degrees 17 | } 18 | \examples{ 19 | rad2deg(2*pi) == 360 20 | } 21 | -------------------------------------------------------------------------------- /R/toGMT.R: -------------------------------------------------------------------------------- 1 | #' Local time to GMT 2 | #' 3 | #' The function transforms a \code{POSIXct} object in any given time zone to GMT. 4 | #' 5 | #' @param time Time, a \code{POSIXct} object. 6 | #' @return A a \code{POSIXct} object, in GMT. 7 | #' 8 | #' @examples 9 | #' time = as.POSIXct("1999-01-01 12:00:00", tz = "Asia/Jerusalem") 10 | #' toGMT(time) 11 | #' 12 | #' @export 13 | 14 | toGMT = function(time) { 15 | as.POSIXct(format.POSIXct(time, tz = "GMT"), tz = "GMT") 16 | } 17 | -------------------------------------------------------------------------------- /man/boston_park.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{boston_park} 5 | \alias{boston_park} 6 | \title{Polygonal layer of a park in Boston} 7 | \format{ 8 | A \code{SpatialPolygons} with a single feature. 9 | } 10 | \usage{ 11 | boston_park 12 | } 13 | \description{ 14 | A \code{SpatialPolygons} object representing the boundaries of a park in Central Boston. 15 | } 16 | \examples{ 17 | boston_park 18 | plot(boston_park, axes = TRUE) 19 | } 20 | \keyword{datasets} 21 | -------------------------------------------------------------------------------- /man/toGMT.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/toGMT.R 3 | \name{toGMT} 4 | \alias{toGMT} 5 | \title{Local time to GMT} 6 | \usage{ 7 | toGMT(time) 8 | } 9 | \arguments{ 10 | \item{time}{Time, a \code{POSIXct} object.} 11 | } 12 | \value{ 13 | A a \code{POSIXct} object, in GMT. 14 | } 15 | \description{ 16 | The function transforms a \code{POSIXct} object in any given time zone to GMT. 17 | } 18 | \examples{ 19 | time = as.POSIXct("1999-01-01 12:00:00", tz = "Asia/Jerusalem") 20 | toGMT(time) 21 | 22 | } 23 | -------------------------------------------------------------------------------- /man/boston_sidewalk.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{boston_sidewalk} 5 | \alias{boston_sidewalk} 6 | \title{Polygonal layer of sidewalks in Boston} 7 | \format{ 8 | A \code{SpatialLinesDataFrame} with 78 features. 9 | } 10 | \usage{ 11 | boston_sidewalk 12 | } 13 | \description{ 14 | A \code{SpatialLinesDataFrame} object representing sidewalks in Central Boston. 15 | } 16 | \examples{ 17 | boston_sidewalk 18 | plot(boston_sidewalk, axes = TRUE) 19 | } 20 | \keyword{datasets} 21 | -------------------------------------------------------------------------------- /man/boston_block.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{boston_block} 5 | \alias{boston_block} 6 | \title{Polygonal layer of a building block in Boston} 7 | \format{ 8 | A \code{SpatialPolygons} with a single feature. 9 | } 10 | \usage{ 11 | boston_block 12 | } 13 | \description{ 14 | A \code{SpatialPolygons} object representing the boundaries of a building block in Central Boston. 15 | } 16 | \examples{ 17 | boston_block 18 | plot(boston_block, axes = TRUE) 19 | } 20 | \keyword{datasets} 21 | -------------------------------------------------------------------------------- /man/build.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{build} 5 | \alias{build} 6 | \title{Polygonal layer of four buildings in Rishon} 7 | \format{ 8 | A \code{SpatialPolygonsDataFrame} with 4 features and 2 attributes: 9 | \describe{ 10 | \item{build_id}{Building ID} 11 | \item{BLDG_HT}{Building height, in meters} 12 | } 13 | } 14 | \usage{ 15 | build 16 | } 17 | \description{ 18 | A \code{SpatialPolygonsDataFrame} object representing the outlines of four buildings located in Rishon-Le-Zion. The attribute \code{BLDG_HT} contains building height, in meters. 19 | } 20 | \examples{ 21 | build 22 | plot(build, axes = TRUE) 23 | } 24 | \keyword{datasets} 25 | -------------------------------------------------------------------------------- /man/classifyAz.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/classifyAz.R 3 | \name{classifyAz} 4 | \alias{classifyAz} 5 | \title{Classify azimuth of line segments} 6 | \usage{ 7 | classifyAz(sl) 8 | } 9 | \arguments{ 10 | \item{sl}{A \code{SpatialLines*} object} 11 | } 12 | \value{ 13 | A \code{numeric} vector with the segment azimuth values (in decimal degrees) 14 | } 15 | \description{ 16 | Classify azimuth of line segments 17 | } 18 | \examples{ 19 | build_seg = toSeg(build[1, ]) 20 | az = classifyAz(build_seg) 21 | plot(build_seg, col = rainbow(4)[cut(az, c(0, 90, 180, 270, 360))]) 22 | raster::text( 23 | # rgeos::gCentroid(build_seg, byid = TRUE), 24 | as(sf::st_centroid(sf::st_geometry(sf::st_as_sf(build_seg))), "Spatial"), 25 | round(az) 26 | ) 27 | 28 | } 29 | -------------------------------------------------------------------------------- /tests/testthat/test_shadowFootprint.R: -------------------------------------------------------------------------------- 1 | library(shadow) 2 | 3 | context("shadowFootprint") 4 | 5 | test_that("Shade footprint calculation is correct", { 6 | expect_equal({ 7 | data(build) 8 | # location = rgeos::gCentroid(build) 9 | location = as(sf::st_geometry(sf::st_centroid(sf::st_union(sf::st_as_sf(build)))), "Spatial") 10 | time = as.POSIXct("2004-12-24 13:30:00", tz = "Asia/Jerusalem") 11 | solar_pos = suntools::solarpos( 12 | matrix(c(34.7767978098526, 31.9665936050395), ncol = 2), 13 | time 14 | ) 15 | footprint = shadowFootprint( 16 | obstacles = build, 17 | obstacles_height_field = "BLDG_HT", 18 | solar_pos = solar_pos 19 | ) 20 | # rgeos::gArea(footprint) 21 | sum(sf::st_area(sf::st_as_sf(footprint))) 22 | 23 | }, 24 | 6513.44739346873 25 | ) 26 | } 27 | ) 28 | -------------------------------------------------------------------------------- /docs/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /man/boston_build.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{boston_build} 5 | \alias{boston_build} 6 | \title{Polygonal layer of three buildings in Boston} 7 | \format{ 8 | A \code{SpatialPolygonsDataFrame} with 10 features and 4 attributes: 9 | \describe{ 10 | \item{objectid}{Building part ID} 11 | \item{build_id}{Building ID} 12 | \item{part_floor}{Number of floors for part} 13 | \item{height_m}{Building height, in meters} 14 | } 15 | } 16 | \usage{ 17 | boston_build 18 | } 19 | \description{ 20 | A \code{SpatialPolygonsDataFrame} object representing the outlines of three buildings located in Central Boston. The attribute \code{height_m} contains building height, in meters. 21 | } 22 | \examples{ 23 | boston_build 24 | plot(boston_build, axes = TRUE) 25 | } 26 | \keyword{datasets} 27 | -------------------------------------------------------------------------------- /man/beersheva_elev.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{beersheva_elev} 5 | \alias{beersheva_elev} 6 | \title{DEM of Ramot neighborhood, Beer-Sheva} 7 | \format{ 8 | A \code{RasterLayer} representing a grid of 1974 raster cells, each cell is a 30*30 meters rectangle. Data source is the Shuttle Radar Topography Mission (SRTM) 1 Arc-Second Global dataset. 9 | } 10 | \usage{ 11 | beersheva_elev 12 | } 13 | \description{ 14 | Digital Elevation Model (DEM) of Ramot neighborhood, Beer-Sheva. Raster values represent elevation above sea level, in meters. 15 | } 16 | \examples{ 17 | beersheva_elev 18 | plot(beersheva_elev) 19 | } 20 | \references{ 21 | \url{https://www.usgs.gov/centers/eros/science/usgs-eros-archive-digital-elevation-shuttle-radar-topography-mission-srtm-1} 22 | } 23 | \keyword{datasets} 24 | -------------------------------------------------------------------------------- /man/shiftAz.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/shiftAz.R 3 | \name{shiftAz} 4 | \alias{shiftAz} 5 | \title{Shift features by azimuth and distance} 6 | \usage{ 7 | shiftAz(object, az, dist) 8 | } 9 | \arguments{ 10 | \item{object}{The object to be shifted.} 11 | 12 | \item{az}{Shift azimuth, in decimal degrees.} 13 | 14 | \item{dist}{Shift distance, in \code{object} projection units.} 15 | } 16 | \value{ 17 | The shifted \code{object}. 18 | } 19 | \description{ 20 | Shift features by azimuth and distance 21 | } 22 | \examples{ 23 | s = c(270, 90, 180, 0) 24 | build_shifted = shiftAz(build, az = s, dist = 2.5) 25 | plot(build) 26 | plot(build_shifted, add = TRUE, border = "red") 27 | # raster::text(rgeos::gCentroid(build, byid = TRUE), s) 28 | raster::text(as(sf::st_centroid(sf::st_geometry(sf::st_as_sf(build))), "Spatial"), s) 29 | 30 | } 31 | -------------------------------------------------------------------------------- /R/sunLocation.R: -------------------------------------------------------------------------------- 1 | # Function to create a 'SpatialPointsDataFrame' object representing sun location 2 | 3 | .sunLocation = function(location, sun_az, sun_elev) { 4 | 5 | # Conditions 6 | stopifnot(class(location) %in% c("SpatialPoints", "SpatialPointsDataFrame")) 7 | 8 | # Average earth-sun distance in meters 9 | dist_m = 149.6 * 10^9 10 | 11 | # To radians 12 | az_rad = deg2rad(90 - sun_az) 13 | sun_elev = deg2rad(sun_elev) 14 | 15 | # Sun height 16 | height = data.frame(height = dist_m * tan(sun_elev)) 17 | 18 | # Attribute table 19 | if(is(location, "SpatialPointsDataFrame")) 20 | data = cbind(location@data, height) else 21 | data = height 22 | 23 | # Sun location 24 | sp::SpatialPointsDataFrame( 25 | coords = raster::shift(location, dist_m * cos(az_rad), dist_m * sin(az_rad)), 26 | data = data 27 | ) 28 | 29 | } 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /man/beersheva_build.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{beersheva_build} 5 | \alias{beersheva_build} 6 | \title{Polygonal layer of 376 buildings in Beer-Sheva} 7 | \format{ 8 | A \code{SpatialPolygonsDataFrame} with 10 features and 4 attributes: 9 | \describe{ 10 | \item{build_id}{Building ID} 11 | \item{floors}{Number of floors for building} 12 | \item{apartments}{Number of apartments} 13 | \item{height_m}{Building height, in meters} 14 | \item{elev}{Elevation above sea level of building base, in meters} 15 | } 16 | } 17 | \usage{ 18 | beersheva_build 19 | } 20 | \description{ 21 | A \code{SpatialPolygonsDataFrame} object representing the outlines of 367 buildings in the Ramot neighborhood, Beer-Sheva. The attribute \code{height_m} contains building height, in meters. 22 | } 23 | \examples{ 24 | beersheva_build 25 | plot(beersheva_build, axes = TRUE) 26 | } 27 | \keyword{datasets} 28 | -------------------------------------------------------------------------------- /man/ray.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ray.R 3 | \name{ray} 4 | \alias{ray} 5 | \title{Line between two points} 6 | \usage{ 7 | ray(from, to) 8 | } 9 | \arguments{ 10 | \item{from}{A \code{SpatialPoints*} object specifying origin.} 11 | 12 | \item{to}{A \code{SpatialPoints*} object specifying destination.} 13 | } 14 | \value{ 15 | A \code{SpatialLines} object. 16 | } 17 | \description{ 18 | The function connects two points into a line segment. 19 | } 20 | \examples{ 21 | # ctr = rgeos::gCentroid(build) 22 | ctr = as(sf::st_centroid(sf::st_union(sf::st_geometry(sf::st_as_sf(build)))), "Spatial") 23 | angles = seq(0, 359, 20) 24 | sun = mapply( 25 | shadow:::.sunLocation, 26 | sun_az = angles, 27 | MoreArgs = list( 28 | location = ctr, 29 | sun_elev = 10) 30 | ) 31 | rays = mapply(ray, MoreArgs = list(from = ctr), to = sun) 32 | rays$makeUniqueIDs = TRUE 33 | rays = do.call(rbind, rays) 34 | plot(rays) 35 | sun = do.call(rbind, sun) 36 | text(sun, as.character(angles)) 37 | 38 | } 39 | -------------------------------------------------------------------------------- /man/toSeg.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/toSeg.R 3 | \name{toSeg} 4 | \alias{toSeg} 5 | \title{Split polygons or lines to segments} 6 | \usage{ 7 | toSeg(x) 8 | } 9 | \arguments{ 10 | \item{x}{A \code{SpatialLines*} or a \code{SpatialPolygons*} object} 11 | } 12 | \value{ 13 | A \code{SpatialLines} object where each segment is represented by a separate feature 14 | } 15 | \description{ 16 | Split lines or polygons to separate segments. 17 | } 18 | \examples{ 19 | seg = toSeg(build[1, ]) 20 | plot(seg, col = sample(rainbow(length(seg)))) 21 | # raster::text(rgeos::gCentroid(seg, byid = TRUE), 1:length(seg)) 22 | raster::text(as(sf::st_centroid(sf::st_geometry(sf::st_as_sf(seg))), "Spatial"), 1:length(seg)) 23 | 24 | # Other data structures 25 | toSeg(geometry(build)) # SpatialPolygons 26 | toSeg(boston_sidewalk) # SpatialLinesDataFrame 27 | toSeg(geometry(boston_sidewalk)) # SpatialLinesDataFrame 28 | 29 | } 30 | \references{ 31 | This function uses a modified version of code from the following 'r-sig-geo' post by Roger Bivand: 32 | \url{https://stat.ethz.ch/pipermail/r-sig-geo/2013-April/017998.html} 33 | } 34 | -------------------------------------------------------------------------------- /man/tmy.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{tmy} 5 | \alias{tmy} 6 | \title{Typical Meteorological Year (TMY) solar radiation in Tel-Aviv} 7 | \format{ 8 | A \code{data.frame} with 8760 rows and 7 columns. 9 | } 10 | \usage{ 11 | tmy 12 | } 13 | \description{ 14 | A table with hourly solar radiation estimates for a typical meteorological year in Tel-Aviv. \itemize{ 15 | \item{\code{time} Time, as \code{character} in the \code{"\%Y-\%m-\%d \%H:\%M:\%S"} format, e.g. \code{"2000-01-01 06:00:00"}}, referring to local time 16 | \item{\code{sun_az} Sun azimuth, in decimal degrees from North} 17 | \item{\code{sun_elev} Sun elevation, in decimal degrees} 18 | \item{\code{solar_normal} Direct Normal Irradiance, in Wh/m^2} 19 | \item{\code{solar_diffuse} Diffuse Horizontal Irradiance, in Wh/m^2} 20 | \item{\code{dbt} Dry-bulb temperature, in Celsius degrees} 21 | \item{\code{ws} Wind speed, in m/s} 22 | } 23 | } 24 | \examples{ 25 | head(tmy) 26 | } 27 | \references{ 28 | \url{https://energyplus.net/weather-location/europe_wmo_region_6/ISR/ISR_Tel.Aviv-Bet.Dagan.401790_MSI} 29 | } 30 | \keyword{datasets} 31 | -------------------------------------------------------------------------------- /man/tmy2.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{tmy2} 5 | \alias{tmy2} 6 | \title{Typical Meteorological Year (TMY) solar radiation in Beer-Sheva} 7 | \format{ 8 | A \code{data.frame} with 8760 rows and 7 columns. 9 | } 10 | \usage{ 11 | tmy2 12 | } 13 | \description{ 14 | A table with hourly solar radiation estimates for a typical meteorological year in Beer-Sheva. \itemize{ 15 | \item{\code{time} Time, as \code{character} in the \code{"\%Y-\%m-\%d \%H:\%M:\%S"} format, e.g. \code{"2000-01-01 06:00:00"}}, referring to local time 16 | \item{\code{sun_az} Sun azimuth, in decimal degrees from North} 17 | \item{\code{sun_elev} Sun elevation, in decimal degrees} 18 | \item{\code{solar_normal} Direct Normal Irradiance, in Wh/m^2} 19 | \item{\code{solar_diffuse} Diffuse Horizontal Irradiance, in Wh/m^2} 20 | \item{\code{dbt} Dry-bulb temperature, in Celsius degrees} 21 | \item{\code{ws} Wind speed, in m/s} 22 | } 23 | } 24 | \examples{ 25 | head(tmy2) 26 | } 27 | \references{ 28 | \url{https://energyplus.net/weather-location/europe_wmo_region_6/ISR/ISR_Beer.Sheva.401900_MSI} 29 | } 30 | \keyword{datasets} 31 | -------------------------------------------------------------------------------- /man/solarpos2.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/solarpos2.R 3 | \name{solarpos2} 4 | \alias{solarpos2} 5 | \title{Calculate solar position(s) for location and time} 6 | \usage{ 7 | solarpos2(location, time) 8 | } 9 | \arguments{ 10 | \item{location}{A \code{Spatial*} or a \code{Raster} object} 11 | 12 | \item{time}{A \code{SpatialLines*} or a \code{SpatialPolygons*} object} 13 | } 14 | \value{ 15 | A \code{matrix} with two columns representing sun position(s); first column is the solar azimuth (in decimal degrees from North), second column is sun elevation (in decimal degrees); rows represent different times corresponding to \code{time} 16 | } 17 | \description{ 18 | This is a wrapper function around \code{suntools::solarpos}, adapted for accepting location as a \code{Spatial*} layer or a \code{Raster}. The function calculates layer centroid, transforms it to lon-lat, then calls \code{suntools::solarpos} to calculate solar position(s) for that point at the given time(s) 19 | } 20 | \examples{ 21 | time = as.POSIXct("2004-12-24 13:30:00", tz = "Asia/Jerusalem") 22 | proj4string(build) = CRS("EPSG:32636") 23 | solarpos2(build, time) 24 | 25 | } 26 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(SVF) 4 | export(classifyAz) 5 | export(coefDirect) 6 | export(deg2rad) 7 | export(inShadow) 8 | export(plotGrid) 9 | export(rad2deg) 10 | export(radiation) 11 | export(ray) 12 | export(shadowFootprint) 13 | export(shadowHeight) 14 | export(shiftAz) 15 | export(solarpos2) 16 | export(surfaceGrid) 17 | export(toGMT) 18 | export(toSeg) 19 | exportMethods(SVF) 20 | exportMethods(inShadow) 21 | exportMethods(shadowHeight) 22 | import(raster) 23 | import(sp) 24 | importFrom(methods,"slot<-") 25 | importFrom(methods,as) 26 | importFrom(methods,is) 27 | importFrom(methods,slot) 28 | importFrom(parallel,makeCluster) 29 | importFrom(parallel,mclapply) 30 | importFrom(parallel,parLapply) 31 | importFrom(sf,st_as_sf) 32 | importFrom(sf,st_buffer) 33 | importFrom(sf,st_cast) 34 | importFrom(sf,st_collection_extract) 35 | importFrom(sf,st_convex_hull) 36 | importFrom(sf,st_distance) 37 | importFrom(sf,st_geometry) 38 | importFrom(sf,st_geometry_type) 39 | importFrom(sf,st_intersection) 40 | importFrom(sf,st_intersects) 41 | importFrom(sf,st_length) 42 | importFrom(sf,st_union) 43 | importFrom(suntools,solarpos) 44 | importFrom(utils,setTxtProgressBar) 45 | importFrom(utils,txtProgressBar) 46 | -------------------------------------------------------------------------------- /man/shadow.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/shadow-package.R 3 | \docType{package} 4 | \name{shadow} 5 | \alias{shadow} 6 | \title{\code{shadow}: R Package for Geometric Shade Calculations} 7 | \description{ 8 | Main functions for calculating:\itemize{ 9 | \item \code{shadowHeight}, Shadow height at individual points or continuous surface 10 | \item \code{shadowFootprint}, Polygonal layer of shadow footprints on the ground 11 | \item \code{SVF}, Sky View Factor (SVF) value at individual points or continuous surface 12 | } 13 | Typical inputs for these functions include:\itemize{ 14 | \item \code{location}, Queried location(s) 15 | \item \code{obstacles}, A polygonal layer of obstacles (e.g. buildings) outline, with height attributes \code{obstacles_height_field} 16 | \item \code{solar_pos}, Solar position (i.e. sun azimuth and elevation angles) 17 | } 18 | The package also provides functions for related preliminary calculations, such as:\itemize{ 19 | \item \code{toSeg}, Converting polygons to line segments 20 | \item \code{classifyAz}, Finding segment azimuth 21 | \item \code{shiftAz}, Shifting segments by azimuth and distance 22 | \item \code{ray}, Constructing a line between two points 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/testthat/test_SVF.R: -------------------------------------------------------------------------------- 1 | library(shadow) 2 | 3 | context("SVF") 4 | 5 | test_that("SVF calculation is correct", { 6 | expect_equal({ 7 | data(build) 8 | # location0 = rgeos::gCentroid(build) 9 | location0 = as(sf::st_geometry(sf::st_centroid(sf::st_union(sf::st_as_sf(build)))), "Spatial") 10 | SVF( 11 | location = location0, 12 | obstacles = build, 13 | obstacles_height_field = "BLDG_HT" 14 | ) 15 | }, 16 | 0.39597205126485 17 | ) 18 | expect_equal({ 19 | data(build) 20 | # location0 = rgeos::gCentroid(build) 21 | location0 = as(sf::st_geometry(sf::st_centroid(sf::st_union(sf::st_as_sf(build)))), "Spatial") 22 | location1 = raster::shift(location0, 0, -15) 23 | SVF( 24 | location = location1, 25 | obstacles = build, 26 | obstacles_height_field = "BLDG_HT" 27 | ) 28 | }, 29 | 0.139491688314422 30 | ) 31 | expect_equal({ 32 | data(build) 33 | # location0 = rgeos::gCentroid(build) 34 | location0 = as(sf::st_geometry(sf::st_centroid(sf::st_union(sf::st_as_sf(build)))), "Spatial") 35 | location2 = raster::shift(location0, -10, 20) 36 | SVF( 37 | location = location2, 38 | obstacles = build, 39 | obstacles_height_field = "BLDG_HT" 40 | ) 41 | }, 42 | 0.770505253636759 43 | ) 44 | } 45 | ) 46 | -------------------------------------------------------------------------------- /R/ray.R: -------------------------------------------------------------------------------- 1 | #' Line between two points 2 | #' 3 | #' The function connects two points into a line segment. 4 | #' 5 | #' @param from A \code{SpatialPoints*} object specifying origin. 6 | #' @param to A \code{SpatialPoints*} object specifying destination. 7 | #' 8 | #' @return A \code{SpatialLines} object. 9 | #' 10 | #' @examples 11 | #' # ctr = rgeos::gCentroid(build) 12 | #' ctr = as(sf::st_centroid(sf::st_union(sf::st_geometry(sf::st_as_sf(build)))), "Spatial") 13 | #' angles = seq(0, 359, 20) 14 | #' sun = mapply( 15 | #' shadow:::.sunLocation, 16 | #' sun_az = angles, 17 | #' MoreArgs = list( 18 | #' location = ctr, 19 | #' sun_elev = 10) 20 | #' ) 21 | #' rays = mapply(ray, MoreArgs = list(from = ctr), to = sun) 22 | #' rays$makeUniqueIDs = TRUE 23 | #' rays = do.call(rbind, rays) 24 | #' plot(rays) 25 | #' sun = do.call(rbind, sun) 26 | #' text(sun, as.character(angles)) 27 | #' 28 | #' @export 29 | 30 | ray = function(from, to) { 31 | 32 | sp::SpatialLines( 33 | list( 34 | sp::Lines( 35 | list( 36 | sp::Line( 37 | rbind( 38 | sp::coordinates(from)[, 1:2], 39 | sp::coordinates(to)[, 1:2] 40 | ) 41 | ) 42 | ), 43 | ID = "A" 44 | ) 45 | ), 46 | proj4string = sp::CRS(sp::proj4string(from)) 47 | ) 48 | 49 | } 50 | -------------------------------------------------------------------------------- /man/plotGrid.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plotGrid.R 3 | \name{plotGrid} 4 | \alias{plotGrid} 5 | \title{Interactive plot for 3D spatial points} 6 | \usage{ 7 | plotGrid(grid, color = c("grey", "red")[as.factor(grid$type)], size = 0.2, ...) 8 | } 9 | \arguments{ 10 | \item{grid}{A three-dimensional \code{SpatialPoints*} object} 11 | 12 | \item{color}{Point color, either a single value or vector corresponding to the number of points. The default values draws "facade" and "roof" points in different colors, assuming these classes appear in a column named \code{type}, as returned by function \code{\link{surfaceGrid}}} 13 | 14 | \item{size}{Point radius, default is \code{0.1}} 15 | 16 | \item{...}{Additional parameters passed to \code{scatterplot3js}} 17 | } 18 | \value{ 19 | An htmlwidget object that is displayed using the object's show or print method. If you don't see your widget plot, try printing it with the print function. (Same as for \code{threejs::scatterplot3js}) 20 | } 21 | \description{ 22 | This is a wrapper around \code{scatterplot3js} from package \code{threejs}. The function adjusts the x, y and z axes so that 1:1:1 proportion are kept and z=0 corresponds to ground level. 23 | } 24 | \examples{ 25 | \dontrun{ 26 | grid = surfaceGrid( 27 | obstacles = build, 28 | obstacles_height_field = "BLDG_HT", 29 | res = 1, 30 | offset = 0.01 31 | ) 32 | plotGrid(grid) 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /R/shadow-package.R: -------------------------------------------------------------------------------- 1 | #' \code{shadow}: R Package for Geometric Shade Calculations 2 | #' 3 | #' Main functions for calculating:\itemize{ 4 | #' \item \code{shadowHeight}, Shadow height at individual points or continuous surface 5 | #' \item \code{shadowFootprint}, Polygonal layer of shadow footprints on the ground 6 | #' \item \code{SVF}, Sky View Factor (SVF) value at individual points or continuous surface 7 | #' } 8 | #' Typical inputs for these functions include:\itemize{ 9 | #' \item \code{location}, Queried location(s) 10 | #' \item \code{obstacles}, A polygonal layer of obstacles (e.g. buildings) outline, with height attributes \code{obstacles_height_field} 11 | #' \item \code{solar_pos}, Solar position (i.e. sun azimuth and elevation angles) 12 | #' } 13 | #' The package also provides functions for related preliminary calculations, such as:\itemize{ 14 | #' \item \code{toSeg}, Converting polygons to line segments 15 | #' \item \code{classifyAz}, Finding segment azimuth 16 | #' \item \code{shiftAz}, Shifting segments by azimuth and distance 17 | #' \item \code{ray}, Constructing a line between two points 18 | #' } 19 | #' 20 | #' @docType package 21 | #' 22 | #' @name shadow 23 | #' 24 | #' @import sp 25 | #' @import raster 26 | #' @importFrom methods as 27 | #' @importFrom methods is 28 | #' @importFrom utils setTxtProgressBar 29 | #' @importFrom utils txtProgressBar 30 | #' @importFrom parallel mclapply 31 | #' @importFrom parallel makeCluster 32 | #' @importFrom parallel parLapply 33 | 34 | NULL 35 | -------------------------------------------------------------------------------- /R/solarpos2.R: -------------------------------------------------------------------------------- 1 | #' Calculate solar position(s) for location and time 2 | #' 3 | #' This is a wrapper function around \code{suntools::solarpos}, adapted for accepting location as a \code{Spatial*} layer or a \code{Raster}. The function calculates layer centroid, transforms it to lon-lat, then calls \code{suntools::solarpos} to calculate solar position(s) for that point at the given time(s) 4 | #' 5 | #' @param location A \code{Spatial*} or a \code{Raster} object 6 | #' @param time A \code{SpatialLines*} or a \code{SpatialPolygons*} object 7 | #' @return A \code{matrix} with two columns representing sun position(s); first column is the solar azimuth (in decimal degrees from North), second column is sun elevation (in decimal degrees); rows represent different times corresponding to \code{time} 8 | #' 9 | #' @examples 10 | #' time = as.POSIXct("2004-12-24 13:30:00", tz = "Asia/Jerusalem") 11 | #' proj4string(build) = CRS("EPSG:32636") 12 | #' solarpos2(build, time) 13 | #' 14 | #' @importFrom suntools solarpos 15 | #' @export 16 | 17 | solarpos2 = function(location, time) { 18 | 19 | # Checks 20 | stopifnot(methods::is(location, "Spatial") | methods::is(location, "Raster")) 21 | .checkTime(time) 22 | 23 | if(methods::is(location, "Raster")) location = raster::rasterToPoints(location, spatial = TRUE) 24 | 25 | # Find centroid 26 | # location_ctr = rgeos::gCentroid(location) 27 | location_ctr = sf::st_as_sf(sf::st_centroid(sf::st_union(sf::st_cast(sf::st_geometry(sf::st_as_sf(location), "POINT"))))) 28 | 29 | # Transform to lon-lat 30 | location_ctr = sf::st_transform(location_ctr, "OGC:CRS84") 31 | 32 | # Calculate solar position 33 | suntools::solarpos(location_ctr, time) 34 | 35 | } 36 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: shadow 2 | Type: Package 3 | Title: Geometric Shadow Calculations 4 | Version: 0.7.2 5 | Authors@R: c( 6 | person("Michael", "Dorman", email = "dorman@post.bgu.ac.il", role = c("aut", "cre")), 7 | person("Evyatar", "Erell", email = "erell@bgu.ac.il", role = c("ctb")), 8 | person("Itai", "Kloog", email = "ikloog@gmail.com", role = c("ctb")), 9 | person("Adi", "Vulkan", email = "vulkan@post.bgu.ac.il", role = c("ctb")), 10 | person("Roger", "Bivand", email = "roger.bivand@nhh.no", role = c("ctb"))) 11 | Description: Functions for calculating: (1) shadow height, (2) logical shadow flag, (3) shadow footprint, (4) Sky View Factor and (5) radiation load. Basic required inputs include a polygonal layer of obstacle outlines along with their heights (i.e. "extruded polygons"), sun azimuth and sun elevation. The package also provides functions for related preliminary calculations: breaking polygons into line segments, determining azimuth of line segments, shifting segments by azimuth and distance, constructing the footprint of a line-of-sight between an observer and the sun, and creating a 3D grid covering the surface area of extruded polygons. 12 | License: MIT + file LICENSE 13 | LazyData: TRUE 14 | Imports: 15 | raster (>= 2.4-15), 16 | sf (>= 1.0.14), 17 | suntools, 18 | methods, 19 | parallel (>= 3.4.0) 20 | Depends: 21 | R (>= 3.5.0), 22 | sp (>= 2.0.0) 23 | RoxygenNote: 7.2.3 24 | Suggests: 25 | R.rsp, 26 | testthat, 27 | reshape2 (>= 1.4.2), 28 | threejs 29 | VignetteBuilder: R.rsp 30 | URL: https://michaeldorman.github.io/shadow/, https://github.com/michaeldorman/shadow/ 31 | BugReports: https://github.com/michaeldorman/shadow/issues/ 32 | Encoding: UTF-8 33 | -------------------------------------------------------------------------------- /R/radiationGrid.R: -------------------------------------------------------------------------------- 1 | .radiationGrid = function( 2 | grid, 3 | obstacles, 4 | obstacles_height_field, 5 | solar_pos, 6 | solar_normal, 7 | solar_diffuse, 8 | returnList, 9 | parallel 10 | ) { 11 | 12 | # Determine shadow 13 | cat("Determining Shadow\n") 14 | s = inShadow( 15 | location = grid, 16 | obstacles = obstacles, 17 | obstacles_height_field = obstacles_height_field, 18 | solar_pos = solar_pos, 19 | parallel = parallel 20 | ) 21 | cat("\nDone\n") 22 | 23 | # Calculate 'direct' radiation coefficient 24 | coef = coefDirect( 25 | type = grid$type, 26 | facade_az = grid$facade_az, 27 | solar_pos = solar_pos 28 | ) 29 | 30 | # Set coefficient to zero where shaded 31 | coef = coef * !s 32 | 33 | # Multiply by 'direct' radiation load 34 | direct = sweep( 35 | x = coef, 36 | MARGIN = 2, 37 | STATS = solar_normal, 38 | FUN = "*" 39 | ) 40 | 41 | # Calculate 'SVF' 42 | cat("Calculating SVF\n") 43 | grid$svf = SVF( 44 | location = grid, 45 | obstacles = obstacles, 46 | obstacles_height_field = obstacles_height_field, 47 | parallel = parallel 48 | ) 49 | cat("Done\n") 50 | 51 | # Calculate 'diffuse' radiation 52 | diffuse = outer(grid$svf, solar_diffuse) 53 | 54 | if(returnList) { 55 | 56 | result = list( 57 | direct = direct, 58 | diffuse = diffuse 59 | ) 60 | 61 | } else { 62 | 63 | # Sum radiation 64 | direct_sum = rowSums(direct) 65 | diffuse_sum = rowSums(diffuse) 66 | total_sum = direct_sum + diffuse_sum 67 | 68 | # Return result 69 | result = data.frame( 70 | svf = grid$svf, 71 | direct = direct_sum, 72 | diffuse = diffuse_sum, 73 | total = total_sum 74 | ) 75 | 76 | } 77 | 78 | return(result) 79 | 80 | } 81 | -------------------------------------------------------------------------------- /R/classifyAz.R: -------------------------------------------------------------------------------- 1 | #' Classify azimuth of line segments 2 | #' 3 | #' @param sl A \code{SpatialLines*} object 4 | #' 5 | #' @return A \code{numeric} vector with the segment azimuth values (in decimal degrees) 6 | #' 7 | #' @examples 8 | #' build_seg = toSeg(build[1, ]) 9 | #' az = classifyAz(build_seg) 10 | #' plot(build_seg, col = rainbow(4)[cut(az, c(0, 90, 180, 270, 360))]) 11 | #' raster::text( 12 | #' # rgeos::gCentroid(build_seg, byid = TRUE), 13 | #' as(sf::st_centroid(sf::st_geometry(sf::st_as_sf(build_seg))), "Spatial"), 14 | #' round(az) 15 | #' ) 16 | #' 17 | #' @export 18 | 19 | classifyAz = function(sl) { 20 | 21 | # If input is not SpatialLines 22 | stopifnot(class(sl) %in% c("SpatialLines", "SpatialLinesDataFrame")) 23 | 24 | # Empty vector for holding results 25 | result = rep(NA, length(sl)) 26 | 27 | # For each feature 28 | for(i in 1:length(sl)) { 29 | 30 | # Select one 31 | s = sl[i, ] 32 | m = s@lines[[1]]@Lines[[1]]@coords 33 | 34 | # If feature is not a segment 35 | if(!nrow(m) == 2) 36 | stop( 37 | "Input contains features which are not segments. 38 | Consider using function 'polToSeg' first." 39 | ) 40 | 41 | # Calculate segment direction 42 | x1 = m[1, 1] 43 | y1 = m[1, 2] 44 | x2 = m[2, 1] 45 | y2 = m[2, 2] 46 | 47 | if(x2 >= x1 & y2 > y1) { 48 | az = 360 - (180 / pi) * atan((y2-y1) / (x2-x1)) 49 | } 50 | if(x2 >= x1 & y2 <= y1) { 51 | az = (180 / pi) * atan((y1-y2) / (x2-x1)) 52 | } 53 | if(x2 < x1 & y2 <= y1) { 54 | az = 180 - (180 / pi) * atan((y1-y2) / (x1-x2)) 55 | } 56 | if(x2 < x1 & y2 > y1) { 57 | az = 180 + (180 / pi) * atan((y2-y1) / (x1-x2)) 58 | } 59 | 60 | result[i] = az 61 | 62 | } 63 | 64 | return(result) 65 | 66 | } 67 | -------------------------------------------------------------------------------- /tests/testthat/test_shadowHeight.R: -------------------------------------------------------------------------------- 1 | library(shadow) 2 | 3 | context("shadowHeight") 4 | 5 | test_that("Shade height calculation is correct", { 6 | expect_false(isTRUE(all.equal({ 7 | data(build) 8 | # location = rgeos::gCentroid(build) 9 | location = as(sf::st_centroid(sf::st_union(sf::st_geometry(sf::st_as_sf(build)))), "Spatial") 10 | solar_pos = matrix( 11 | c(208.733303840646, 241.006416412884, 262.037856636259, 12 | 28.7994405393304, 1.81958332207186, -34.5606455413366), 13 | ncol = 2) 14 | shadowHeight( 15 | location = location, 16 | obstacles = build, 17 | obstacles_height_field = "BLDG_HT", 18 | solar_pos = solar_pos 19 | ) 20 | }, 21 | matrix( 22 | c(19.8645079285858, 22.3721120166259, Inf), # Matrix of sun positions 23 | nrow = 1, 24 | ncol = 3 25 | ) 26 | ))) 27 | 28 | expect_equal({ 29 | data(build) 30 | # location = rgeos::gCentroid(build) 31 | location = as(sf::st_centroid(sf::st_union(sf::st_geometry(sf::st_as_sf(build)))), "Spatial") 32 | solar_pos = matrix(c(343.665362102935, -81.0986528138936), ncol = 2) 33 | shadowHeight( 34 | location = location, 35 | obstacles = build, 36 | obstacles_height_field = "BLDG_HT", 37 | solar_pos = solar_pos 38 | ) 39 | }, 40 | matrix(Inf, nrow = 1, ncol = 1) # Night = Infinite shade height at night 41 | ) 42 | expect_equal({ 43 | data(build) 44 | # location = rgeos::gCentroid(build) 45 | location = as(sf::st_geometry(sf::st_centroid(sf::st_union(sf::st_as_sf(build)))), "Spatial") 46 | solar_pos = matrix(c(0, 80), ncol = 2) 47 | shadowHeight( 48 | location = location, 49 | obstacles = build, 50 | obstacles_height_field = "BLDG_HT", 51 | solar_pos = solar_pos 52 | ) 53 | }, 54 | matrix(NA, nrow = 1, ncol = 1) # No shade at noon 55 | ) 56 | } 57 | ) 58 | -------------------------------------------------------------------------------- /docs/bootstrap-toc.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/) 3 | * Copyright 2015 Aidan Feldman 4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ 5 | 6 | /* modified from https://github.com/twbs/bootstrap/blob/94b4076dd2efba9af71f0b18d4ee4b163aa9e0dd/docs/assets/css/src/docs.css#L548-L601 */ 7 | 8 | /* All levels of nav */ 9 | nav[data-toggle='toc'] .nav > li > a { 10 | display: block; 11 | padding: 4px 20px; 12 | font-size: 13px; 13 | font-weight: 500; 14 | color: #767676; 15 | } 16 | nav[data-toggle='toc'] .nav > li > a:hover, 17 | nav[data-toggle='toc'] .nav > li > a:focus { 18 | padding-left: 19px; 19 | color: #563d7c; 20 | text-decoration: none; 21 | background-color: transparent; 22 | border-left: 1px solid #563d7c; 23 | } 24 | nav[data-toggle='toc'] .nav > .active > a, 25 | nav[data-toggle='toc'] .nav > .active:hover > a, 26 | nav[data-toggle='toc'] .nav > .active:focus > a { 27 | padding-left: 18px; 28 | font-weight: bold; 29 | color: #563d7c; 30 | background-color: transparent; 31 | border-left: 2px solid #563d7c; 32 | } 33 | 34 | /* Nav: second level (shown on .active) */ 35 | nav[data-toggle='toc'] .nav .nav { 36 | display: none; /* Hide by default, but at >768px, show it */ 37 | padding-bottom: 10px; 38 | } 39 | nav[data-toggle='toc'] .nav .nav > li > a { 40 | padding-top: 1px; 41 | padding-bottom: 1px; 42 | padding-left: 30px; 43 | font-size: 12px; 44 | font-weight: normal; 45 | } 46 | nav[data-toggle='toc'] .nav .nav > li > a:hover, 47 | nav[data-toggle='toc'] .nav .nav > li > a:focus { 48 | padding-left: 29px; 49 | } 50 | nav[data-toggle='toc'] .nav .nav > .active > a, 51 | nav[data-toggle='toc'] .nav .nav > .active:hover > a, 52 | nav[data-toggle='toc'] .nav .nav > .active:focus > a { 53 | padding-left: 28px; 54 | font-weight: 500; 55 | } 56 | 57 | /* from https://github.com/twbs/bootstrap/blob/e38f066d8c203c3e032da0ff23cd2d6098ee2dd6/docs/assets/css/src/docs.css#L631-L634 */ 58 | nav[data-toggle='toc'] .nav > .active > ul { 59 | display: block; 60 | } 61 | -------------------------------------------------------------------------------- /R/shiftAz.R: -------------------------------------------------------------------------------- 1 | #' Shift features by azimuth and distance 2 | #' 3 | #' @param object The object to be shifted. 4 | #' @param az Shift azimuth, in decimal degrees. 5 | #' @param dist Shift distance, in \code{object} projection units. 6 | #' 7 | #' @return The shifted \code{object}. 8 | #' 9 | #' @examples 10 | #' s = c(270, 90, 180, 0) 11 | #' build_shifted = shiftAz(build, az = s, dist = 2.5) 12 | #' plot(build) 13 | #' plot(build_shifted, add = TRUE, border = "red") 14 | #' # raster::text(rgeos::gCentroid(build, byid = TRUE), s) 15 | #' raster::text(as(sf::st_centroid(sf::st_geometry(sf::st_as_sf(build))), "Spatial"), s) 16 | #' 17 | #' @export 18 | 19 | shiftAz = function(object, az, dist) { 20 | 21 | # Check lengths of 'az' and 'dist' 22 | object_features = length(geometry(object)) 23 | if( 24 | !(length(az) == 1 | length(az) == object_features) 25 | ) { 26 | stop("'az' needs to be of length 1 or same length as 'object'") 27 | } 28 | if( 29 | !(length(dist) == 1 | length(dist) == object_features) 30 | ) { 31 | stop("'az' needs to be of length 1 or same length as 'object'") 32 | } 33 | 34 | # Recycle if necessary 35 | if(length(az == 1)) az = rep(az, length(object)) 36 | if(length(dist == 1)) dist = rep(dist, length(object)) 37 | 38 | az_rad = deg2rad(90 - az) 39 | 40 | object_shifted = list() 41 | 42 | for(i in 1:length(object)) { 43 | 44 | object_shifted[[i]] = raster::shift(object[i, ], dist[i] * cos(az_rad[i]), dist[i] * sin(az_rad[i])) 45 | 46 | # Workaround for 'raster::shift' behavior of setting 'coord' dimnames 47 | if(class(object) %in% c("SpatialLines", "SpatialLinesDataFrame")) { 48 | for(j in 1:length(object_shifted[[i]]@lines[[1]]@Lines)) { 49 | dimnames(object_shifted[[i]]@lines[[1]]@Lines[[j]]@coords) = NULL 50 | } 51 | } 52 | if(class(object) %in% c("SpatialPolygons", "SpatialPolygonsDataFrame")) { 53 | for(j in 1:length(object_shifted[[i]]@polygons[[1]]@Polygons)) { 54 | dimnames(object_shifted[[i]]@polygons[[1]]@Polygons[[j]]@coords) = NULL 55 | } 56 | } 57 | 58 | } 59 | 60 | if(class(object) %in% c("SpatialLines", "SpatialLinesDataFrame", "SpatialPolygons", "SpatialPolygonsDataFrame")) { 61 | object_shifted = mapply(spChFIDs, object_shifted, row.names(object)) 62 | } 63 | 64 | object_shifted = do.call(rbind, object_shifted) 65 | 66 | } 67 | 68 | -------------------------------------------------------------------------------- /docs/docsearch.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | // register a handler to move the focus to the search bar 4 | // upon pressing shift + "/" (i.e. "?") 5 | $(document).on('keydown', function(e) { 6 | if (e.shiftKey && e.keyCode == 191) { 7 | e.preventDefault(); 8 | $("#search-input").focus(); 9 | } 10 | }); 11 | 12 | $(document).ready(function() { 13 | // do keyword highlighting 14 | /* modified from https://jsfiddle.net/julmot/bL6bb5oo/ */ 15 | var mark = function() { 16 | 17 | var referrer = document.URL ; 18 | var paramKey = "q" ; 19 | 20 | if (referrer.indexOf("?") !== -1) { 21 | var qs = referrer.substr(referrer.indexOf('?') + 1); 22 | var qs_noanchor = qs.split('#')[0]; 23 | var qsa = qs_noanchor.split('&'); 24 | var keyword = ""; 25 | 26 | for (var i = 0; i < qsa.length; i++) { 27 | var currentParam = qsa[i].split('='); 28 | 29 | if (currentParam.length !== 2) { 30 | continue; 31 | } 32 | 33 | if (currentParam[0] == paramKey) { 34 | keyword = decodeURIComponent(currentParam[1].replace(/\+/g, "%20")); 35 | } 36 | } 37 | 38 | if (keyword !== "") { 39 | $(".contents").unmark({ 40 | done: function() { 41 | $(".contents").mark(keyword); 42 | } 43 | }); 44 | } 45 | } 46 | }; 47 | 48 | mark(); 49 | }); 50 | }); 51 | 52 | /* Search term highlighting ------------------------------*/ 53 | 54 | function matchedWords(hit) { 55 | var words = []; 56 | 57 | var hierarchy = hit._highlightResult.hierarchy; 58 | // loop to fetch from lvl0, lvl1, etc. 59 | for (var idx in hierarchy) { 60 | words = words.concat(hierarchy[idx].matchedWords); 61 | } 62 | 63 | var content = hit._highlightResult.content; 64 | if (content) { 65 | words = words.concat(content.matchedWords); 66 | } 67 | 68 | // return unique words 69 | var words_uniq = [...new Set(words)]; 70 | return words_uniq; 71 | } 72 | 73 | function updateHitURL(hit) { 74 | 75 | var words = matchedWords(hit); 76 | var url = ""; 77 | 78 | if (hit.anchor) { 79 | url = hit.url_without_anchor + '?q=' + escape(words.join(" ")) + '#' + hit.anchor; 80 | } else { 81 | url = hit.url + '?q=' + escape(words.join(" ")); 82 | } 83 | 84 | return url; 85 | } 86 | -------------------------------------------------------------------------------- /R/toSeg.R: -------------------------------------------------------------------------------- 1 | #' Split polygons or lines to segments 2 | #' 3 | #' Split lines or polygons to separate segments. 4 | #' 5 | #' @param x A \code{SpatialLines*} or a \code{SpatialPolygons*} object 6 | #' @return A \code{SpatialLines} object where each segment is represented by a separate feature 7 | #' 8 | #' @references 9 | #' This function uses a modified version of code from the following 'r-sig-geo' post by Roger Bivand: 10 | #' \url{https://stat.ethz.ch/pipermail/r-sig-geo/2013-April/017998.html} 11 | #' 12 | #' @examples 13 | #' seg = toSeg(build[1, ]) 14 | #' plot(seg, col = sample(rainbow(length(seg)))) 15 | #' # raster::text(rgeos::gCentroid(seg, byid = TRUE), 1:length(seg)) 16 | #' raster::text(as(sf::st_centroid(sf::st_geometry(sf::st_as_sf(seg))), "Spatial"), 1:length(seg)) 17 | #' 18 | #' # Other data structures 19 | #' toSeg(geometry(build)) # SpatialPolygons 20 | #' toSeg(boston_sidewalk) # SpatialLinesDataFrame 21 | #' toSeg(geometry(boston_sidewalk)) # SpatialLinesDataFrame 22 | #' 23 | #' @export 24 | 25 | toSeg = function(x) { 26 | 27 | # Check 'x' class 28 | .checkLinePoly(x) 29 | 30 | seg = list() 31 | 32 | for(f in 1:length(x)) { 33 | 34 | # Select one polygon 35 | dat = x[f, ] 36 | 37 | # Convert to 'SpatialLines' *** Depends on package 'sp' *** 38 | dat_l = as(dat, "SpatialLines") 39 | 40 | # Split to line segments 41 | cSl = sp::coordinates(dat_l) 42 | in_nrows = lapply(cSl, function(x) sapply(x, nrow)) 43 | outn = sapply(in_nrows, function(y) sum(y - 1)) 44 | res = vector(mode = "list", length = outn) 45 | i = 1 46 | for (j in seq(along=cSl)) { 47 | for (k in seq(along=cSl[[j]])) { 48 | for (l in 1:(nrow(cSl[[j]][[k]])-1)) { 49 | res[[i]] = cSl[[j]][[k]][l:(l+1),] 50 | i = i + 1 51 | } 52 | } 53 | } 54 | res1 = vector(mode = "list", length = outn) 55 | for (i in seq(along = res)) 56 | res1[[i]] = sp::Lines(list(sp::Line(res[[i]])), paste(f, i)) 57 | seg1 = sp::SpatialLines(res1, proj4string = sp::CRS(sp::proj4string(x))) 58 | 59 | # Add polygon attribute table entry to each segment 60 | if(class(x) %in% c("SpatialLinesDataFrame", "SpatialPolygonsDataFrame")) { 61 | attr_table = 62 | x@data[rep(f, length(seg1)), , drop = FALSE] 63 | rownames(attr_table) = paste(f, 1:length(res)) 64 | seg1 = sp::SpatialLinesDataFrame( 65 | seg1, 66 | data = attr_table, 67 | match.ID = FALSE 68 | ) 69 | } 70 | 71 | seg[[f]] = seg1 72 | 73 | } 74 | 75 | seg = do.call(rbind, seg) 76 | 77 | seg 78 | 79 | } 80 | -------------------------------------------------------------------------------- /R/plotGrid.R: -------------------------------------------------------------------------------- 1 | #' Interactive plot for 3D spatial points 2 | #' 3 | #' This is a wrapper around \code{scatterplot3js} from package \code{threejs}. The function adjusts the x, y and z axes so that 1:1:1 proportion are kept and z=0 corresponds to ground level. 4 | #' 5 | #' @param grid A three-dimensional \code{SpatialPoints*} object 6 | #' @param color Point color, either a single value or vector corresponding to the number of points. The default values draws "facade" and "roof" points in different colors, assuming these classes appear in a column named \code{type}, as returned by function \code{\link{surfaceGrid}} 7 | #' @param size Point radius, default is \code{0.1} 8 | #' @param ... Additional parameters passed to \code{scatterplot3js} 9 | #' 10 | #' @return An htmlwidget object that is displayed using the object's show or print method. If you don't see your widget plot, try printing it with the print function. (Same as for \code{threejs::scatterplot3js}) 11 | #' 12 | #' @examples 13 | #' \dontrun{ 14 | #' grid = surfaceGrid( 15 | #' obstacles = build, 16 | #' obstacles_height_field = "BLDG_HT", 17 | #' res = 1, 18 | #' offset = 0.01 19 | #' ) 20 | #' plotGrid(grid) 21 | #' } 22 | #' 23 | #' @export 24 | 25 | plotGrid = function(grid, color = c("grey", "red")[as.factor(grid$type)], size = 0.2, ...) { 26 | 27 | # Check 'threejs' package availability 28 | if(!requireNamespace("threejs")) 29 | stop("You need to install the 'threejs' package to be able to use this function") 30 | 31 | # Check if 'pnt' is '3D point' object 32 | if(!class(grid) %in% c("SpatialPoints", "SpatialPointsDataFrame")) 33 | stop("'grid' is not 'SpatialPoints*'") 34 | if(dimensions(grid) != 3) 35 | stop("'grid' is not three-dimensional") 36 | 37 | # Get 3D point coordinates 38 | coords = coordinates(grid) 39 | 40 | # Determine X, Y and Z range 41 | x_range = range(coords[, 1]) 42 | y_range = range(coords[, 2]) 43 | z_range = range(coords[, 3]) 44 | x_range_diff = diff(x_range) 45 | y_range_diff = diff(y_range) 46 | z_range_diff = diff(z_range) 47 | x_mid = mean(x_range) 48 | y_mid = mean(y_range) 49 | z_mid = mean(z_range) 50 | max_range = max(c(x_range_diff, y_range_diff, z_range_diff)) 51 | 52 | # Plot 53 | threejs::scatterplot3js( 54 | x = coords[, 1], 55 | y = coords[, 2], 56 | z = coords[, 3], 57 | xlim = c(x_mid - max_range / 2, x_mid + max_range / 2), 58 | ylim = c(y_mid - max_range / 2, y_mid + max_range / 2), 59 | zlim = c(0, z_mid + max_range), 60 | num.ticks = c(6, 6, 6), 61 | x.ticklabs = rep("", 6), 62 | y.ticklabs = rep("", 6), 63 | color = color, 64 | size = size, 65 | ... 66 | ) 67 | 68 | } 69 | 70 | -------------------------------------------------------------------------------- /data-raw/ethis::use_data(build)[201~: -------------------------------------------------------------------------------- 1 | use_data package:usethis R Documentation 2 | 3 | _C_r_e_a_t_e _p_a_c_k_a_g_e _d_a_t_a 4 | 5 | _D_e_s_c_r_i_p_t_i_o_n: 6 | 7 | ‘use_data()’ makes it easy to save package data in the correct 8 | format. I recommend you save scripts that generate package data in 9 | ‘data-raw’: use ‘use_data_raw()’ to set it up. You also need to 10 | document exported datasets. 11 | 12 | _U_s_a_g_e: 13 | 14 | use_data( 15 | ..., 16 | internal = FALSE, 17 | overwrite = FALSE, 18 | compress = "bzip2", 19 | version = 2 20 | ) 21 | 22 | use_data_raw(name = "DATASET", open = rlang::is_interactive()) 23 | 24 | _A_r_g_u_m_e_n_t_s: 25 | 26 | ...: Unquoted names of existing objects to save. 27 | 28 | internal: If ‘FALSE’, saves each object in its own ‘.rda’ file in the 29 | data/ directory. These data files bypass the usual export 30 | mechanism and are available whenever the package is loaded 31 | (or via ‘data()’ if ‘LazyData’ is not true). 32 | 33 | If ‘TRUE’, stores all objects in a single ‘R/sysdata.rda’ 34 | file. Objects in this file follow the usual export rules. 35 | Note that this means they will be exported if you are using 36 | the common ‘exportPattern()’ rule which exports all objects 37 | except for those that start with ‘.’. 38 | 39 | overwrite: By default, ‘use_data()’ will not overwrite existing files. 40 | If you really want to do so, set this to ‘TRUE’. 41 | 42 | compress: Choose the type of compression used by ‘save()’. Should be 43 | one of "gzip", "bzip2", or "xz". 44 | 45 | version: The serialization format version to use. The default, 2, was 46 | the default format from R 1.4.0 to 3.5.3. Version 3 became 47 | the default from R 3.6.0 and can only be read by R versions 48 | 3.5.0 and higher. 49 | 50 | name: Name of the dataset to be prepared for inclusion in the 51 | package. 52 | 53 | open: Open the newly created file for editing? Happens in RStudio, 54 | if applicable, or via ‘utils::file.edit()’ otherwise. 55 | 56 | _S_e_e _A_l_s_o: 57 | 58 | The data chapter of R Packages. 59 | 60 | _E_x_a_m_p_l_e_s: 61 | 62 | ## Not run: 63 | 64 | x <- 1:10 65 | y <- 1:100 66 | 67 | use_data(x, y) # For external use 68 | use_data(x, y, internal = TRUE) # For internal use 69 | ## End(Not run) 70 | 71 | ## Not run: 72 | 73 | use_data_raw("daisy") 74 | ## End(Not run) 75 | 76 | 77 | -------------------------------------------------------------------------------- /man/coefDirect.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/coefDirect.R 3 | \name{coefDirect} 4 | \alias{coefDirect} 5 | \title{Coefficient of Direct Normal Irradiance reduction} 6 | \usage{ 7 | coefDirect(type, facade_az, solar_pos) 8 | } 9 | \arguments{ 10 | \item{type}{\code{character}, specifying surface type. All values must be either \code{"roof"} or \code{"facade"}} 11 | 12 | \item{facade_az}{Facade azimuth, in decimal degrees from North. Only relevant for \code{type="facade"}} 13 | 14 | \item{solar_pos}{A matrix with two columns representing sun position(s); first column is the solar azimuth (in decimal degrees from North), second column is sun elevation (in decimal degrees); rows represent different positions (e.g. at different times of day)} 15 | } 16 | \value{ 17 | Numeric vector of coefficients, to be multiplied by the direct beam radiation values. The vector length is the same as the length of the longest input (see \strong{Note} below) 18 | } 19 | \description{ 20 | This function calculates the coefficient of reduction in Direct Normal Irradiance load due to angle of incidence. For example, a coefficient of 1 is obtained when the sun is perpendicular to the surface. 21 | } 22 | \note{ 23 | All four arguments are recycled to match each other's length. For example, you may specify a single \code{type} value of \code{"roof"} or \code{"facade"} and a single \code{facade_az} value, but multiple \code{sun_az} and \code{sun_elev} values, for calculating the coefficients for a single location given different positions of the sun, etc. 24 | } 25 | \examples{ 26 | # Basic usage 27 | coefDirect(type = "facade", facade_az = 180, solar_pos = matrix(c(210, 30), ncol = 2)) 28 | 29 | # Demonstration - Direct beam radiation coefficient on 'facades' 30 | sun_az = seq(270, 90, by = -5) 31 | sun_elev = seq(0, 90, by = 5) 32 | solar_pos = expand.grid(sun_az = sun_az, sun_elev = sun_elev) 33 | solar_pos$coef = coefDirect(type = "facade", facade_az = 180, solar_pos = as.matrix(solar_pos))[1, ] 34 | coef = reshape2::acast(solar_pos, sun_az ~ sun_elev, value.var = "coef") 35 | image( 36 | 180 - sun_az, sun_elev, coef, 37 | col = rev(heat.colors(10)), 38 | breaks = seq(0, 1, 0.1), 39 | asp = 1, 40 | xlab = "Facade azimuth - Sun azimuth (deg)", 41 | ylab = "Sun elevation (deg)", 42 | main = "Facade - Coefficient of Direct Normal Irradiance" 43 | ) 44 | contour(180 - sun_az, sun_elev, coef, add = TRUE) 45 | 46 | # Demonstration - Direct beam radiation coefficient on 'roofs' 47 | solar_pos$coef = coefDirect(type = "roof", facade_az = 180, solar_pos = as.matrix(solar_pos))[1, ] 48 | coef = reshape2::acast(solar_pos, sun_az ~ sun_elev, value.var = "coef") 49 | image( 50 | 180 - sun_az, sun_elev, coef, 51 | col = rev(heat.colors(10)), 52 | breaks = seq(0, 1, 0.1), 53 | asp = 1, 54 | xlab = "Facade azimuth - Sun azimuth (deg)", 55 | ylab = "Sun elevation (deg)", 56 | main = "Roof - Coefficient of Direct Normal Irradiance" 57 | ) 58 | contour(180 - sun_az, sun_elev, coef, add = TRUE) 59 | } 60 | -------------------------------------------------------------------------------- /man/surfaceGrid.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/surfaceGrid.R 3 | \name{surfaceGrid} 4 | \alias{surfaceGrid} 5 | \title{Create grid of 3D points covering the 'facades' and 'roofs' of obstacles} 6 | \usage{ 7 | surfaceGrid(obstacles, obstacles_height_field, res, offset = 0.01) 8 | } 9 | \arguments{ 10 | \item{obstacles}{A \code{SpatialPolygonsDataFrame} object specifying the obstacles outline} 11 | 12 | \item{obstacles_height_field}{Name of attribute in \code{obstacles} with extrusion height for each feature} 13 | 14 | \item{res}{Required grid resolution, in CRS units} 15 | 16 | \item{offset}{Offset between grid points and facade (horizontal distance) or between grid points and roof (vertical distance).} 17 | } 18 | \value{ 19 | A 3D \code{SpatialPointsDataFrame} layer, including all attributes of the original obstacles each surface point corresponds to, followed by six new attributes:\itemize{ 20 | \item{\code{obs_id} Unique consecutive ID for each feature in \code{obstacles}} 21 | \item{\code{type} Either \code{"facade"} or \code{"roof"}} 22 | \item{\code{seg_id} Unique consecutive ID for each facade segment (only for 'facade' points)} 23 | \item{\code{xy_id} Unique consecutive ID for each ground location (only for 'facade' points)} 24 | \item{\code{facade_az} The azimuth of the corresponding facade, in decimal degrees (only for 'facade' points)} 25 | } 26 | } 27 | \description{ 28 | The function creates a grid of 3D points covering the given obstacles at specified resolution. Such a grid can later on be used to quantify the shaded / non-shaded proportion of the obstacles surface area. 29 | } 30 | \note{ 31 | The reason for introducing an offset is to avoid ambiguity as for whether the grid points are "inside" or "outside" of the obstacle. With an offset all grid points are "outside" of the building and thus not intersecting it. \code{offset} should be given in CRS units; default is 0.01. 32 | } 33 | \examples{ 34 | grid = surfaceGrid( 35 | obstacles = build, 36 | obstacles_height_field = "BLDG_HT", 37 | res = 2 38 | ) 39 | plot(grid) 40 | plot(grid, pch = 1, lwd = 0.1, col = "black", add = TRUE) 41 | 42 | # When 'res/2' is larger then height, facade will be left unsampled 43 | build_small = build 44 | build_small$BLDG_HT = 1 45 | grid = surfaceGrid( 46 | obstacles = build_small, 47 | obstacles_height_field = "BLDG_HT", 48 | res = 2 49 | ) 50 | plot(grid) 51 | plot(grid, pch = 1, lwd = 0.1, col = "black", add = TRUE) 52 | table(grid$type) 53 | 54 | grid = surfaceGrid( 55 | obstacles = build_small, 56 | obstacles_height_field = "BLDG_HT", 57 | res = 2.00001 # res/2 > h 58 | ) 59 | plot(grid) 60 | plot(grid, pch = 1, lwd = 0.1, col = "black", add = TRUE) 61 | table(grid$type) 62 | 63 | # When input already contains 'obs_id', 'type', 'seg_id', 'xy_id', 'facade_az' or 'ZZZ' 64 | build2 = build 65 | build2$ZZZ = 1 66 | grid = surfaceGrid( 67 | obstacles = build2, 68 | obstacles_height_field = "BLDG_HT", 69 | res = 2 70 | ) 71 | 72 | } 73 | \seealso{ 74 | Function \code{\link{plotGrid}} to visualize grid. 75 | } 76 | -------------------------------------------------------------------------------- /tests/testthat/test_radiation.R: -------------------------------------------------------------------------------- 1 | library(shadow) 2 | 3 | context("radiation") 4 | 5 | test_that("radiation calculation is incorrect", { 6 | expect_false(isTRUE(all.equal({ 7 | 8 | data(build) 9 | this_CRS = slot(build, "proj4string") 10 | data(tmy) 11 | 12 | # grid 13 | set.seed(17) 14 | grid = new( 15 | "SpatialPointsDataFrame", 16 | data = structure( 17 | list( 18 | build_id = c("407", "407", "407"), 19 | BLDG_HT = c(21.38, 20 | 21.38, 21.38), 21 | obs_id = c(1L, 1L, 1L), 22 | type = c("facade", "roof", 23 | "facade"), 24 | seg_id = c(6L, NA, 21L), 25 | xy_id = c(18L, NA, 50L), 26 | facade_az = c(321.427363755109, NA, 136.951204869441) 27 | ), 28 | .Names = c( 29 | "build_id", 30 | "BLDG_HT", 31 | "obs_id", 32 | "type", 33 | "seg_id", 34 | "xy_id", 35 | "facade_az" 36 | ), 37 | row.names = c(400L, 15L, 687L), 38 | class = "data.frame" 39 | ) 40 | , 41 | coords.nrs = numeric(0) 42 | , 43 | coords = structure( 44 | c( 45 | 667855.102115946, 46 | 667862.288856074, 47 | 667871.369943073, 48 | 3538112.62815535, 49 | 3538101.47268511, 50 | 3538095.63665798, 51 | 11, 52 | 21.39, 53 | 21 54 | ), 55 | .Dim = c(3L, 3L), 56 | .Dimnames = list(NULL, c("x1", "x2", "h")) 57 | ) 58 | , 59 | bbox = structure( 60 | c( 61 | 667855.102115946, 62 | 3538095.63665798, 63 | 11, 64 | 667871.369943073, 65 | 3538112.62815535, 66 | 21.39 67 | ), 68 | .Dim = c(3L, 2L), 69 | .Dimnames = list(c("x1", "x2", "h"), c("min", "max")) 70 | ) 71 | , 72 | proj4string = this_CRS # new("CRS", projargs = "+proj=utm +zone=36 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0") 73 | ) 74 | 75 | solar_pos = tmy[, c("sun_az", "sun_elev")] 76 | solar_pos = as.matrix(solar_pos) 77 | 78 | radiation( 79 | grid = grid, 80 | obstacles = build, 81 | obstacles_height_field = "BLDG_HT", 82 | solar_pos = solar_pos[8:17, , drop = FALSE], 83 | solar_normal = tmy$solar_normal[8:17], 84 | solar_diffuse = tmy$solar_diffuse[8:17] 85 | ) 86 | }, 87 | 88 | structure( 89 | list( 90 | svf = c(0.484475008535678, 0.998328963274761,0.491760588247644), 91 | direct = c(21.0822885075299, 2755.36321510678,2913.69184551694), 92 | diffuse = c(250.473579412946, 516.136074013052,254.240224124032), 93 | total = c(271.555867920476, 3271.49928911984,3167.93206964097) 94 | ), 95 | .Names = c("svf", "direct", "diffuse", "total"), 96 | row.names = c(NA,-3L), 97 | class = "data.frame" 98 | ) 99 | 100 | ))) 101 | 102 | } 103 | ) 104 | -------------------------------------------------------------------------------- /man/shadowFootprint.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/shadowFootprint.R 3 | \name{shadowFootprint} 4 | \alias{shadowFootprint} 5 | \alias{shadowFootprint,SpatialPolygonsDataFrame-method} 6 | \title{Shadow footprint on the ground} 7 | \usage{ 8 | \S4method{shadowFootprint}{SpatialPolygonsDataFrame}( 9 | obstacles, 10 | obstacles_height_field, 11 | solar_pos = solarpos2(obstacles, time), 12 | time = NULL, 13 | b = 0.01 14 | ) 15 | } 16 | \arguments{ 17 | \item{obstacles}{A \code{SpatialPolygonsDataFrame} object specifying the obstacles outline} 18 | 19 | \item{obstacles_height_field}{Name of attribute in \code{obstacles} with extrusion height for each feature} 20 | 21 | \item{solar_pos}{A \code{matrix} with one row and two columns; first column is the solar azimuth (in decimal degrees from North), second column is sun elevation (in decimal degrees)} 22 | 23 | \item{time}{When \code{solar_pos} is unspecified, \code{time} can be passed to automatically calculate \code{solar_pos} based on the time and the centroid of \code{obstacles}, using function \code{suntools::solarpos}. In such case \code{obstacles} must have a defined CRS (not \code{NA}). The \code{time} value must be a \code{POSIXct} or \code{POSIXlt} object} 24 | 25 | \item{b}{Buffer size for shadow footprints of individual segments of a given polygon; used to eliminate minor internal holes in the resulting shadow polygon.} 26 | } 27 | \value{ 28 | A \code{SpatialPolygonsDataFrame} object representing shadow footprint, plus buildings outline. Object length is the same as that of the input \code{obstacles}, with an individual footprint feature for each obstacle. 29 | } 30 | \description{ 31 | Creates a polygonal layer of shadow footprints on the ground, taking into account:\itemize{ 32 | \item{Obstacles outline (\code{obstacles}), given by a polygonal layer with a height attribute (\code{obstacles_height_field})} 33 | \item{Sun position (\code{solar_pos}), given by azimuth and elevation angles} 34 | } 35 | The calculation method was inspired by Morel Weisthal's MSc thesis at the Ben-Gurion University of the Negev. 36 | } 37 | \examples{ 38 | time = as.POSIXct("2004-12-24 13:30:00", tz = "Asia/Jerusalem") 39 | proj4string(build) = CRS("EPSG:32636") 40 | location_geo = matrix(c(34.7767978098526, 31.9665936050395), ncol = 2) 41 | solar_pos = suntools::solarpos(location_geo, time) 42 | footprint1 = ## Using 'solar_pos' 43 | shadowFootprint( 44 | obstacles = build, 45 | obstacles_height_field = "BLDG_HT", 46 | solar_pos = solar_pos 47 | ) 48 | footprint2 = ## Using 'time' 49 | shadowFootprint( 50 | obstacles = build, 51 | obstacles_height_field = "BLDG_HT", 52 | time = time 53 | ) 54 | all.equal(footprint1, footprint2) 55 | footprint = footprint1 56 | plot(footprint, col = adjustcolor("lightgrey", alpha.f = 0.5)) 57 | plot(build, add = TRUE, col = "darkgrey") 58 | 59 | } 60 | \references{ 61 | Weisthal, M. (2014). Assessment of potential energy savings in Israel through climate-aware residential building design (MSc Thesis, Ben-Gurion University of the Negev). 62 | \url{https://www.dropbox.com/s/bztnh1fi9znmswj/Thesis_Morel_Weisthal.pdf?dl=1} 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [![CRAN\_Status\_Badge](http://www.r-pkg.org/badges/version-ago/shadow)](https://cran.r-project.org/package=shadow) 5 | [![CRAN\_Downloads\_Badge](http://cranlogs.r-pkg.org/badges/last-month/shadow)](https://cran.r-project.org/package=shadow) 6 | 7 | ### Introduction 8 | 9 | `shadow` is an R package for geometric shadow calculations in an urban 10 | environment. A detailed description can be found in the [R Journal paper 11 | (2019)](https://journal.r-project.org/archive/2019/RJ-2019-024/RJ-2019-024.pdf): 12 | 13 | [![Abstract](README-abstract.png)](https://journal.r-project.org/archive/2019/RJ-2019-024/RJ-2019-024.pdf) 14 | 15 | ### Installation 16 | 17 | CRAN version: 18 | 19 | ``` r 20 | install.packages("shadow") 21 | ``` 22 | 23 | GitHub version: 24 | 25 | ``` r 26 | install.packages("remotes") 27 | remotes::install_github("michaeldorman/shadow") 28 | ``` 29 | 30 | ## Documentation 31 | 32 | The complete documentation can be found at 33 | . 34 | 35 | ### Quick demo 36 | 37 | ``` r 38 | library(shadow) 39 | #> Loading required package: sp 40 | library(raster) 41 | 42 | # Point 43 | location = rgeos::gCentroid(build) 44 | 45 | # Time 46 | time = as.POSIXct( 47 | "2004-12-24 13:30:00", 48 | tz = "Asia/Jerusalem" 49 | ) 50 | 51 | # Location in geographical coordinates 52 | proj4string(location) = CRS("+init=epsg:32636") 53 | location_geo = sp::spTransform( 54 | location, 55 | CRS("+init=epsg:4326") 56 | ) 57 | crs(location) = NA 58 | 59 | # Solar position 60 | solar_pos = maptools::solarpos( 61 | crds = location_geo, 62 | dateTime = time 63 | ) 64 | solar_pos 65 | #> [,1] [,2] 66 | #> [1,] 208.7333 28.79944 67 | 68 | # Shadow height at a single point 69 | h = shadowHeight( 70 | location = location, 71 | obstacles = build, 72 | obstacles_height_field = "BLDG_HT", 73 | solar_pos = solar_pos 74 | ) 75 | 76 | # Result 77 | h 78 | #> [,1] 79 | #> [1,] 19.86451 80 | 81 | # Visualization 82 | sun = shadow:::.sunLocation( 83 | location = location, 84 | sun_az = solar_pos[1, 1], 85 | sun_elev = solar_pos[1, 2] 86 | ) 87 | sun_ray = ray(from = location, to = sun) 88 | build_outline = as(build, "SpatialLinesDataFrame") 89 | inter = rgeos::gIntersection(build_outline, sun_ray) 90 | plot(build) 91 | plot(sun_ray, add = TRUE, col = "yellow") 92 | plot(location, add = TRUE) 93 | text(location, paste(round(h, 2), "m"), pos = 3) 94 | plot(inter, add = TRUE, col = "red") 95 | ``` 96 | 97 | ![](README-demo1-1.png) 98 | 99 | ``` r 100 | 101 | # Raster template 102 | ext = as(extent(build)+50, "SpatialPolygons") 103 | r = raster(ext, res = 2) 104 | 105 | # Shadow height surface 106 | height_surface = shadowHeight( 107 | location = r, 108 | obstacles = build, 109 | obstacles_height_field = "BLDG_HT", 110 | solar_pos = solar_pos, 111 | parallel = 2 112 | ) 113 | 114 | # Visualization 115 | plot(height_surface, col = grey(seq(0.9, 0.2, -0.01))) 116 | contour(height_surface, add = TRUE) 117 | plot(build, add = TRUE, border = "red") 118 | text(rgeos::gCentroid(build, byid = TRUE), build$BLDG_HT) 119 | text(location, paste(round(h, 2), "m"), pos = 3, col = "red", font = 2) 120 | plot(sun_ray, add = TRUE, col = "yellow") 121 | plot(inter, add = TRUE, col = "red") 122 | plot(location, add = TRUE) 123 | ``` 124 | 125 | ![](README-demo1-2.png) 126 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | [![CRAN_Status_Badge](http://www.r-pkg.org/badges/version-ago/shadow)](https://cran.r-project.org/package=shadow) 8 | [![CRAN_Downloads_Badge](http://cranlogs.r-pkg.org/badges/last-month/shadow)](https://cran.r-project.org/package=shadow) 9 | 10 | 11 | ```{r, echo=FALSE} 12 | knitr::opts_chunk$set( 13 | collapse = TRUE, 14 | comment = "#>", 15 | fig.path = "README-" 16 | ) 17 | ``` 18 | 19 | ### Introduction 20 | 21 | `shadow` is an R package for geometric shadow calculations in an urban environment. A detailed description can be found in the [R Journal paper (2019)](https://journal.r-project.org/archive/2019/RJ-2019-024/RJ-2019-024.pdf): 22 | 23 | [![Abstract](README-abstract.png)](https://journal.r-project.org/archive/2019/RJ-2019-024/RJ-2019-024.pdf) 24 | 25 | ### Installation 26 | 27 | CRAN version: 28 | 29 | ```{r installation1, eval=FALSE} 30 | install.packages("shadow") 31 | ``` 32 | 33 | GitHub version: 34 | 35 | ```{r installation2, eval=FALSE} 36 | install.packages("remotes") 37 | remotes::install_github("michaeldorman/shadow") 38 | ``` 39 | 40 | ## Documentation 41 | 42 | The complete documentation can be found at [https://michaeldorman.github.io/shadow/](https://michaeldorman.github.io/shadow/). 43 | 44 | ### Quick demo 45 | 46 | ```{r demo1} 47 | library(shadow) 48 | library(raster) 49 | 50 | # Point 51 | location = rgeos::gCentroid(build) 52 | 53 | # Time 54 | time = as.POSIXct( 55 | "2004-12-24 13:30:00", 56 | tz = "Asia/Jerusalem" 57 | ) 58 | 59 | # Location in geographical coordinates 60 | proj4string(location) = CRS("+init=epsg:32636") 61 | location_geo = sp::spTransform( 62 | location, 63 | CRS("+init=epsg:4326") 64 | ) 65 | crs(location) = NA 66 | 67 | # Solar position 68 | solar_pos = maptools::solarpos( 69 | crds = location_geo, 70 | dateTime = time 71 | ) 72 | solar_pos 73 | 74 | # Shadow height at a single point 75 | h = shadowHeight( 76 | location = location, 77 | obstacles = build, 78 | obstacles_height_field = "BLDG_HT", 79 | solar_pos = solar_pos 80 | ) 81 | 82 | # Result 83 | h 84 | 85 | # Visualization 86 | sun = shadow:::.sunLocation( 87 | location = location, 88 | sun_az = solar_pos[1, 1], 89 | sun_elev = solar_pos[1, 2] 90 | ) 91 | sun_ray = ray(from = location, to = sun) 92 | build_outline = as(build, "SpatialLinesDataFrame") 93 | inter = rgeos::gIntersection(build_outline, sun_ray) 94 | plot(build) 95 | plot(sun_ray, add = TRUE, col = "yellow") 96 | plot(location, add = TRUE) 97 | text(location, paste(round(h, 2), "m"), pos = 3) 98 | plot(inter, add = TRUE, col = "red") 99 | 100 | # Raster template 101 | ext = as(extent(build)+50, "SpatialPolygons") 102 | r = raster(ext, res = 2) 103 | 104 | # Shadow height surface 105 | height_surface = shadowHeight( 106 | location = r, 107 | obstacles = build, 108 | obstacles_height_field = "BLDG_HT", 109 | solar_pos = solar_pos, 110 | parallel = 2 111 | ) 112 | 113 | # Visualization 114 | plot(height_surface, col = grey(seq(0.9, 0.2, -0.01))) 115 | contour(height_surface, add = TRUE) 116 | plot(build, add = TRUE, border = "red") 117 | text(rgeos::gCentroid(build, byid = TRUE), build$BLDG_HT) 118 | text(location, paste(round(h, 2), "m"), pos = 3, col = "red", font = 2) 119 | plot(sun_ray, add = TRUE, col = "yellow") 120 | plot(inter, add = TRUE, col = "red") 121 | plot(location, add = TRUE) 122 | ``` 123 | 124 | ```{r, include=FALSE} 125 | x = list.files(pattern = "^README-.*\\.png$") 126 | file.copy(x, "docs/", overwrite = TRUE) 127 | ``` 128 | -------------------------------------------------------------------------------- /docs/pkgdown.js: -------------------------------------------------------------------------------- 1 | /* http://gregfranko.com/blog/jquery-best-practices/ */ 2 | (function($) { 3 | $(function() { 4 | 5 | $('.navbar-fixed-top').headroom(); 6 | 7 | $('body').css('padding-top', $('.navbar').height() + 10); 8 | $(window).resize(function(){ 9 | $('body').css('padding-top', $('.navbar').height() + 10); 10 | }); 11 | 12 | $('[data-toggle="tooltip"]').tooltip(); 13 | 14 | var cur_path = paths(location.pathname); 15 | var links = $("#navbar ul li a"); 16 | var max_length = -1; 17 | var pos = -1; 18 | for (var i = 0; i < links.length; i++) { 19 | if (links[i].getAttribute("href") === "#") 20 | continue; 21 | // Ignore external links 22 | if (links[i].host !== location.host) 23 | continue; 24 | 25 | var nav_path = paths(links[i].pathname); 26 | 27 | var length = prefix_length(nav_path, cur_path); 28 | if (length > max_length) { 29 | max_length = length; 30 | pos = i; 31 | } 32 | } 33 | 34 | // Add class to parent
  • , and enclosing
  • if in dropdown 35 | if (pos >= 0) { 36 | var menu_anchor = $(links[pos]); 37 | menu_anchor.parent().addClass("active"); 38 | menu_anchor.closest("li.dropdown").addClass("active"); 39 | } 40 | }); 41 | 42 | function paths(pathname) { 43 | var pieces = pathname.split("/"); 44 | pieces.shift(); // always starts with / 45 | 46 | var end = pieces[pieces.length - 1]; 47 | if (end === "index.html" || end === "") 48 | pieces.pop(); 49 | return(pieces); 50 | } 51 | 52 | // Returns -1 if not found 53 | function prefix_length(needle, haystack) { 54 | if (needle.length > haystack.length) 55 | return(-1); 56 | 57 | // Special case for length-0 haystack, since for loop won't run 58 | if (haystack.length === 0) { 59 | return(needle.length === 0 ? 0 : -1); 60 | } 61 | 62 | for (var i = 0; i < haystack.length; i++) { 63 | if (needle[i] != haystack[i]) 64 | return(i); 65 | } 66 | 67 | return(haystack.length); 68 | } 69 | 70 | /* Clipboard --------------------------*/ 71 | 72 | function changeTooltipMessage(element, msg) { 73 | var tooltipOriginalTitle=element.getAttribute('data-original-title'); 74 | element.setAttribute('data-original-title', msg); 75 | $(element).tooltip('show'); 76 | element.setAttribute('data-original-title', tooltipOriginalTitle); 77 | } 78 | 79 | if(ClipboardJS.isSupported()) { 80 | $(document).ready(function() { 81 | var copyButton = ""; 82 | 83 | $(".examples, div.sourceCode").addClass("hasCopyButton"); 84 | 85 | // Insert copy buttons: 86 | $(copyButton).prependTo(".hasCopyButton"); 87 | 88 | // Initialize tooltips: 89 | $('.btn-copy-ex').tooltip({container: 'body'}); 90 | 91 | // Initialize clipboard: 92 | var clipboardBtnCopies = new ClipboardJS('[data-clipboard-copy]', { 93 | text: function(trigger) { 94 | return trigger.parentNode.textContent; 95 | } 96 | }); 97 | 98 | clipboardBtnCopies.on('success', function(e) { 99 | changeTooltipMessage(e.trigger, 'Copied!'); 100 | e.clearSelection(); 101 | }); 102 | 103 | clipboardBtnCopies.on('error', function() { 104 | changeTooltipMessage(e.trigger,'Press Ctrl+C or Command+C to copy'); 105 | }); 106 | }); 107 | } 108 | })(window.jQuery || window.$) 109 | -------------------------------------------------------------------------------- /R/shadowHeightPnt.R: -------------------------------------------------------------------------------- 1 | #' @importFrom sf st_intersection 2 | #' @importFrom sf st_intersects 3 | #' @importFrom sf st_distance 4 | #' @importFrom sf st_buffer 5 | #' @importFrom sf st_collection_extract 6 | #' @importFrom sf st_geometry_type 7 | #' @importFrom sf st_cast 8 | #' @importFrom sf st_as_sf 9 | #' @importFrom methods as 10 | 11 | 12 | .shadowHeightPnt = function( 13 | location, 14 | # surface, 15 | obstacles, 16 | obstacles_height_field, 17 | solar_pos, 18 | obstacles_outline, 19 | b = 0.01 20 | ) { 21 | 22 | # If sun above the horizon 23 | if(solar_pos[, 2] <= 0) shade_height = Inf else { # There is sunlight 24 | 25 | # Sun position 26 | sun = .sunLocation( 27 | location = location, 28 | sun_az = solar_pos[, 1], 29 | sun_elev = solar_pos[, 2] 30 | ) 31 | 32 | # 'Line of sight' between sun and grid point 33 | sun_ray = shadow::ray(from = location, to = sun) 34 | 35 | # Intersections with buildings outline 36 | # inter = rgeos::gIntersection(obstacles_outline, sun_ray) 37 | inter = sf::st_intersection(sf::st_geometry(sf::st_as_sf(location)), sf::st_geometry(sf::st_as_sf(sun))) 38 | 39 | # No intersections means there is no shade 40 | # if(is.null(inter)) shade_height = NA else { 41 | if(length(inter) == 0L) shade_height = NA else { 42 | 43 | # If some of the intersections are lines 44 | # if(is(inter, "SpatialCollections")) { 45 | if(length(grep("GEOMETRY", sf::st_geometry_type(inter))) > 0) { 46 | 47 | # lin = inter@lineobj 48 | lin = sf::st_cast(sf::st_collection_extract(inter, "LINESTRING"), "POINT") 49 | # inter = inter@pointobj 50 | inter = sf::st_collection_extract(inter, "POINT") 51 | 52 | # for(lin_i in 1:length(lin)) { 53 | # 54 | # lin_pnt = lin[lin_i, ] 55 | # lin_pnt = sp::coordinates(lin_pnt)[[1]][[1]] 56 | # lin_pnt = sp::SpatialPoints( 57 | # lin_pnt, 58 | # proj4string = sp::CRS(sp::proj4string(inter)) 59 | # ) 60 | # inter = sp::rbind.SpatialPoints(inter, lin_pnt) 61 | # } 62 | 63 | inter = as(c(st_geometry(inter), st_geometry(lin)), "Spatial") 64 | 65 | } 66 | 67 | # Set row names 68 | row.names(inter) = 1:nrow(inter) 69 | 70 | # Extract building data for each intersection 71 | inter = 72 | SpatialPointsDataFrame( 73 | inter, 74 | sp::over( 75 | inter, 76 | # rgeos::gBuffer( 77 | # obstacles_outline[, obstacles_height_field], 78 | # byid = TRUE, 79 | # width = b 80 | # ), 81 | as(sf::st_buffer( 82 | sf::st_as_sf(obstacles_outline[, obstacles_height_field]), 83 | dist = b), 84 | "Spatial"), 85 | fn = max) 86 | ) 87 | 88 | # Distance between examined location and intersections 89 | # inter$dist = rgeos::gDistance(inter, location, byid = TRUE)[1, ] 90 | inter$dist = sf::st_distance(sf::st_as_sf(inter), sf::st_as_sf(location)) 91 | # Shadow height calculation 92 | inter$shade_fall = inter$dist * tan(deg2rad(solar_pos[, 2])) 93 | inter$shade_height = 94 | inter@data[, obstacles_height_field] - inter$shade_fall 95 | shade_height = max(inter$shade_height) 96 | 97 | # Assign NA when there is no shadow 98 | 99 | # (1) If point is on a building & shadow_height < building_height 100 | # if(rgeos::gIntersects(location, obstacles)) { 101 | if(sf::st_intersects( 102 | sf::st_union( 103 | sf::st_as_sf(location) 104 | ), 105 | sf::st_union( 106 | sf::st_as_sf(obstacles) 107 | ))) { 108 | build_height = sp::over(location, obstacles)[, obstacles_height_field] 109 | if(shade_height <= build_height) 110 | shade_height = NA 111 | } else # (2) If point is on ground & shadow_height < 0 112 | if(shade_height <= 0) shade_height = NA 113 | 114 | } 115 | 116 | } 117 | 118 | return(shade_height) 119 | 120 | } 121 | 122 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | ## shadow 0.3.0 (2017-03-31) 2 | 3 | * Initial complete release 4 | 5 | ## shadow 0.3.2 (2017-05-29) 6 | 7 | * Fixed 'toSeg' behavior when 'x' has single attribute 8 | * Fixed 'shiftAz' behavior when 'object' is 'SpatialPoints*' 9 | * 'parallel' support in functions 'shadowHeight' and 'SVF' 10 | 11 | ## shadow 0.3.3 (2017-06-14) 12 | 13 | * Fixed missing drawing in vignette 14 | 15 | ## shadow 0.3.5 (2017-08-17) 16 | 17 | * Minor documentation update 18 | * SVF also works for 3D points (above ground) 19 | 20 | ## shadow 0.4.0 (2017-11-05) 21 | 22 | * Correction in SVF calculation 23 | * Updated SVF tests and documentation 24 | * Expanded SVF example based on Erell et al. (2012) 25 | 26 | ## shadow 0.4.5 (2018-01-04) 27 | 28 | * Added 'surfaceGrid' function 29 | * Added 'plotGrid3D' function 30 | * Added 'inShadow' function 31 | * Corrections in documentation 32 | 33 | ## shadow 0.5.0 (2018-03-06) 34 | 35 | * Fixed attribute propagation in 'surfaceGrid' 36 | * Added 'tmy' (Typical Meteorological Year) dataset 37 | * Added 'coefDirect' function 38 | * Fixed bug when using 'solar_pos' with >1 rows in 'inShadow' 39 | * Added progress bar to 'inShadow' 40 | * Added 'radiation' function 41 | * Removed the 'message' parameter 42 | * Added test for 'inShadow' 43 | 44 | ## shadow 0.5.3 (2018-05-16) 45 | 46 | * Minor change in 'radiation' example 47 | * Expanded package vignette 48 | * Added test for 'radiation' 49 | * Added 'radius' parameter to 'radiation' for restricted obstacles search 50 | 51 | ## shadow 0.5.5 (2018-07-19) 52 | 53 | * 'returnList' argument in 'radiation' 54 | * Fixed mistakes in the 'time' column of 'tmy' 55 | * Added temperature and wind speed columns in 'tmy' 56 | * Update vignette to comply with new 'tmy' table 57 | 58 | ## shadow 0.5.7 (2018-09-29) 59 | 60 | * Added 'beersheva' sample dataset with Beer-Sheva buildings 61 | * Added 'tmy2' sample dataset with Beer-Sheva TMY data 62 | * Added 'elev' sample dataset with Beer-Sheva elevation 63 | * Fixed 'Obstacles outline union' step in 'surfaceGrid' function to work with polygons with holes 64 | 65 | ## shadow 0.5.9 (2018-12-04) 66 | 67 | * Added 'flowlength' function 68 | * Added 'row.names=NULL' in 'coefDirect' to avoid row names warning 69 | * 'surfaceGrid' now returns 'roof' only points when res/2 > h, instead of error 70 | 71 | ## shadow 0.6.0 (2019-01-07) R-Journal paper 72 | 73 | * Added 'solarpos2' helper function 74 | * Added 'time' parameter in functions 'shadowHeight', 'inShadow', 'shadowFootprint' and 'radiation' 75 | * Renamed datasets ("rishon" -> "build", etc.) 76 | * Updated vignette 77 | 78 | ## shadow 0.6.1 (2019-06-03) 79 | 80 | * Fixed compatibility with 'shift' in new version of 'raster' 81 | 82 | ## shadow 0.6.2 (2019-09-07) 83 | 84 | * Input to 'surfaceGrid' is checked for absence of reserved column names: 'obs_id', 'type', 'seg_id', 'xy_id', 'facade_az' and 'ZZZ' 85 | 86 | ## shadow 0.6.3 (2019-11-11) 87 | 88 | * Replaced vignette with final version of R-Journal paper 89 | 90 | ## shadow 0.6.4 (2020-02-03) 91 | 92 | * Replaced 'class(x) == "matrix"' to comply with R syntax 93 | 94 | ## shadow 0.6.4.1 (2020-04-01) 95 | 96 | * Automatic update by Kurt Hornik to reduce PDF size 97 | 98 | ## shadow 0.6.7 (2020-06-13) 99 | 100 | * Removed 'plyr' dependency 101 | * Removed function 'flowlength' (moved to package 'geobgu') 102 | * Fixed 'shadowFootprint' error when the polygon has duplicated points by removing zero-length segments 103 | * Reduced vignette PDF size 104 | 105 | ## shadow 0.6.8 (2020-08-25) 106 | 107 | * Replaced sample 'sp' layers to comply with WKT2 CRS specification 108 | 109 | ## shadow 0.6.9 (2020-10-10) 110 | 111 | * Revision of CRS in sample data (by Roger Bivand) 112 | * Added 'pkgdown' site 113 | * Set CRS of 'build' to 'NA' to avoid check errors 114 | 115 | ## shadow 0.7.0 116 | 117 | * Added revised vignette 'introduction2' 118 | 119 | ## shadow 0.7.2 120 | 121 | * Fix CRS warnings 122 | * Switch from 'sp' to 'sf' 123 | 124 | ## shadow 0.8 125 | 126 | * Add new tests 127 | * Set 'surfacegrid' class and define 'print' and 'plot' methods for it 128 | * Pass obstacles height as 'units' and check for agreement with CRS units 129 | * Return 'shadowHeight' as 'units' matrix 130 | * Visibility algorithm (e.g. https://www.redblobgames.com/articles/visibility/) 131 | -------------------------------------------------------------------------------- /R/checkInputs.R: -------------------------------------------------------------------------------- 1 | ################################################################ 2 | 3 | .checkTime = function(time) { 4 | if(!any(class(time) %in% "POSIXt")) 5 | stop("'time' must be of class 'POSIXct' or 'POSIXlt'") 6 | } 7 | 8 | ################################################################ 9 | 10 | .checkLocation = function(location, length1 = TRUE) { 11 | 12 | # Check that 'location' is of length 1 13 | if(length1 & length(location) != 1) 14 | stop("'location' should be of length 1") 15 | 16 | # Check projected 17 | if(!is.na(proj4string(location)) & !is.projected(location)) 18 | stop("'location' not in projected CRS") 19 | 20 | if(!class(location) %in% c("SpatialPoints", "SpatialPointsDataFrame")) 21 | stop("'location' is not 'SpatialPoints*'") 22 | 23 | } 24 | 25 | ################################################################ 26 | 27 | .checkSolarPos = function(solar_pos, length1) { 28 | 29 | # Matrix 30 | if(!is(solar_pos, "matrix")) 31 | stop("'solar_pos' must be a 'matrix' object") 32 | 33 | # 1-row 34 | if(length1 & nrow(solar_pos) > 1) 35 | stop("'solar_pos' must have a single row (sun position)") 36 | 37 | # 2-column 38 | if(ncol(solar_pos) != 2) 39 | stop("'solar_pos' must have two columns (azimuth + elevation)") 40 | 41 | # Azimuth in [0,360] 42 | if(any(solar_pos[, 1] < 0) | any(solar_pos[, 1] > 360)) 43 | stop("Sun azimuth should be a number in [0, 360]") 44 | 45 | # Elevation in [-90,90] 46 | if(any(solar_pos[, 2] < -90) | any(solar_pos[, 2] > 90)) 47 | stop("Sun elevation should be a number in [-90, 90]") 48 | 49 | } 50 | 51 | ################################################################ 52 | 53 | .checkObstacles = function(obstacles, obstacles_height_field) { 54 | 55 | # Check projected 56 | if(!is.na(proj4string(obstacles)) & !is.projected(obstacles)) 57 | stop("'obstacles' not in projected CRS") 58 | 59 | # Check class 60 | if(!inherits(obstacles, "SpatialPolygonsDataFrame")) 61 | stop("'obstacles' is not 'SpatialPolygonsDataFrame'") 62 | 63 | # Check that height fields exist 64 | if(!obstacles_height_field %in% names(obstacles)) 65 | stop("'obstacles_height_field' not found in attribute table of 'obstacles'") 66 | 67 | # Check that height values are positive 68 | if(any(obstacles@data[, obstacles_height_field] < 0)) 69 | stop("'obstacles_height_field' cannot contain negative values") 70 | 71 | } 72 | 73 | ################################################################ 74 | 75 | .checkSolarRad = function(solar_normal, solar_diffuse, solar_pos) { 76 | 77 | if(!is.vector(solar_normal) | !is.numeric(solar_normal)) 78 | stop("'solar_normal' must be a numeric vector") 79 | 80 | if(!is.vector(solar_diffuse) | !is.numeric(solar_diffuse)) 81 | stop("'solar_diffuse' must be a numeric vector") 82 | 83 | if(!all.equal(length(solar_normal), length(solar_diffuse), nrow(solar_pos))) 84 | stop("'solar_normal', 'solar_diffuse' and 'solar_pos' must have same length") 85 | 86 | } 87 | 88 | ################################################################ 89 | 90 | .checkGrid = function(grid) { 91 | 92 | if(class(grid) != c("SpatialPointsDataFrame")) 93 | stop("'grid' must be 'SpatialPointsDataFrame'") 94 | 95 | if(dimensions(grid) != 3) 96 | stop("'grid' must be three-dimensional") 97 | 98 | if(!"type" %in% colnames(grid@data)) 99 | stop("'grid' must contain an attribute named 'type'") 100 | 101 | if(!"facade_az" %in% colnames(grid@data)) 102 | stop("'grid' must contain an attribute named 'facade_az'") 103 | 104 | # Azimuth in [0,360] 105 | if(any(!is.na(grid$facade_az) & (grid$facade_az < 0 | grid$facade_az > 360))) 106 | stop("'grid$facade_az' should be numeric in [0, 360]") 107 | 108 | if(any(!grid$type %in% c("roof", "facade"))) 109 | stop("All values of 'type' attribute must be 'roof' or 'facade'") 110 | 111 | } 112 | 113 | ################################################################ 114 | 115 | .checkLinePoly = function(x) { 116 | 117 | if(!class(x) %in% c( 118 | "SpatialLines", 119 | "SpatialLinesDataFrame", 120 | "SpatialPolygons", 121 | "SpatialPolygonsDataFrame") 122 | ) { 123 | 124 | stop("Input must be 'SpatialLines*' or 'SpatialPolygons*' object") 125 | 126 | } 127 | 128 | } 129 | 130 | ################################################################ 131 | -------------------------------------------------------------------------------- /R/coefDirect.R: -------------------------------------------------------------------------------- 1 | #' Coefficient of Direct Normal Irradiance reduction 2 | #' 3 | #' This function calculates the coefficient of reduction in Direct Normal Irradiance load due to angle of incidence. For example, a coefficient of 1 is obtained when the sun is perpendicular to the surface. 4 | #' 5 | #' @param type \code{character}, specifying surface type. All values must be either \code{"roof"} or \code{"facade"} 6 | #' @param facade_az Facade azimuth, in decimal degrees from North. Only relevant for \code{type="facade"} 7 | #' @param solar_pos A matrix with two columns representing sun position(s); first column is the solar azimuth (in decimal degrees from North), second column is sun elevation (in decimal degrees); rows represent different positions (e.g. at different times of day) 8 | #' 9 | #' @return Numeric vector of coefficients, to be multiplied by the direct beam radiation values. The vector length is the same as the length of the longest input (see \strong{Note} below) 10 | #' 11 | #' @note All four arguments are recycled to match each other's length. For example, you may specify a single \code{type} value of \code{"roof"} or \code{"facade"} and a single \code{facade_az} value, but multiple \code{sun_az} and \code{sun_elev} values, for calculating the coefficients for a single location given different positions of the sun, etc. 12 | #' @export 13 | #' 14 | #' @examples 15 | #' # Basic usage 16 | #' coefDirect(type = "facade", facade_az = 180, solar_pos = matrix(c(210, 30), ncol = 2)) 17 | #' 18 | #' # Demonstration - Direct beam radiation coefficient on 'facades' 19 | #' sun_az = seq(270, 90, by = -5) 20 | #' sun_elev = seq(0, 90, by = 5) 21 | #' solar_pos = expand.grid(sun_az = sun_az, sun_elev = sun_elev) 22 | #' solar_pos$coef = coefDirect(type = "facade", facade_az = 180, solar_pos = as.matrix(solar_pos))[1, ] 23 | #' coef = reshape2::acast(solar_pos, sun_az ~ sun_elev, value.var = "coef") 24 | #' image( 25 | #' 180 - sun_az, sun_elev, coef, 26 | #' col = rev(heat.colors(10)), 27 | #' breaks = seq(0, 1, 0.1), 28 | #' asp = 1, 29 | #' xlab = "Facade azimuth - Sun azimuth (deg)", 30 | #' ylab = "Sun elevation (deg)", 31 | #' main = "Facade - Coefficient of Direct Normal Irradiance" 32 | #' ) 33 | #' contour(180 - sun_az, sun_elev, coef, add = TRUE) 34 | #' 35 | #' # Demonstration - Direct beam radiation coefficient on 'roofs' 36 | #' solar_pos$coef = coefDirect(type = "roof", facade_az = 180, solar_pos = as.matrix(solar_pos))[1, ] 37 | #' coef = reshape2::acast(solar_pos, sun_az ~ sun_elev, value.var = "coef") 38 | #' image( 39 | #' 180 - sun_az, sun_elev, coef, 40 | #' col = rev(heat.colors(10)), 41 | #' breaks = seq(0, 1, 0.1), 42 | #' asp = 1, 43 | #' xlab = "Facade azimuth - Sun azimuth (deg)", 44 | #' ylab = "Sun elevation (deg)", 45 | #' main = "Roof - Coefficient of Direct Normal Irradiance" 46 | #' ) 47 | #' contour(180 - sun_az, sun_elev, coef, add = TRUE) 48 | 49 | coefDirect = function(type, facade_az, solar_pos) { 50 | 51 | # Match 'type' and 'facade_az' length 52 | stopifnot(length(type) == 1 || length(type) == length(facade_az)) 53 | if(length(type) == 1) type = rep(type, length(facade_az)) 54 | 55 | # Discard 'solar_pos' 56 | colnames(solar_pos) = NULL 57 | 58 | # Result 59 | result = matrix(nrow = length(facade_az), ncol = nrow(solar_pos)) 60 | 61 | # Loop over times 62 | for(i in 1:ncol(result)) { 63 | 64 | # Recycle values 65 | dat = data.frame( 66 | type, 67 | facade_az, 68 | sun_az = solar_pos[i, 1], 69 | sun_elev = solar_pos[i, 2], 70 | coef = NA, 71 | stringsAsFactors = FALSE, 72 | row.names = NULL 73 | ) 74 | 75 | # Azimuth difference 76 | dat$az_diff = 77 | ifelse( 78 | abs(dat$sun_az - dat$facade_az) > 180, 79 | 360 - abs(dat$sun_az - dat$facade_az), 80 | abs(dat$sun_az - dat$facade_az) 81 | ) 82 | 83 | # For 'roof' azimuth difference is 0 84 | dat$az_diff = ifelse( 85 | dat$type == "roof", 86 | 0, 87 | dat$az_diff 88 | ) 89 | 90 | # Elevation difference 91 | dat$elev_diff = ifelse( 92 | dat$type == "roof", 93 | 90 - dat$sun_elev, 94 | dat$sun_elev 95 | ) 96 | 97 | # To radians 98 | dat$az_diff_r = deg2rad(dat$az_diff) 99 | dat$elev_diff_r = deg2rad(dat$elev_diff) 100 | 101 | # Coefficient 102 | dat$coef = cos(dat$az_diff_r) * cos(dat$elev_diff_r) 103 | 104 | # Special case - Sun below horizon 105 | dat$coef[dat$sun_elev < 0] = 0 106 | 107 | # Special case - Sun "behind" facade or in zenith 108 | dat$coef[dat$type == "facade" & (dat$az_diff >= 90 | dat$sun_elev == 90)] = 0 109 | 110 | # fill 'result' column 111 | result[, i] = dat$coef 112 | 113 | } 114 | 115 | # Return coefficients 116 | return(result) 117 | 118 | } 119 | 120 | 121 | -------------------------------------------------------------------------------- /docs/bootstrap-toc.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/) 3 | * Copyright 2015 Aidan Feldman 4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ 5 | (function() { 6 | 'use strict'; 7 | 8 | window.Toc = { 9 | helpers: { 10 | // return all matching elements in the set, or their descendants 11 | findOrFilter: function($el, selector) { 12 | // http://danielnouri.org/notes/2011/03/14/a-jquery-find-that-also-finds-the-root-element/ 13 | // http://stackoverflow.com/a/12731439/358804 14 | var $descendants = $el.find(selector); 15 | return $el.filter(selector).add($descendants).filter(':not([data-toc-skip])'); 16 | }, 17 | 18 | generateUniqueIdBase: function(el) { 19 | var text = $(el).text(); 20 | var anchor = text.trim().toLowerCase().replace(/[^A-Za-z0-9]+/g, '-'); 21 | return anchor || el.tagName.toLowerCase(); 22 | }, 23 | 24 | generateUniqueId: function(el) { 25 | var anchorBase = this.generateUniqueIdBase(el); 26 | for (var i = 0; ; i++) { 27 | var anchor = anchorBase; 28 | if (i > 0) { 29 | // add suffix 30 | anchor += '-' + i; 31 | } 32 | // check if ID already exists 33 | if (!document.getElementById(anchor)) { 34 | return anchor; 35 | } 36 | } 37 | }, 38 | 39 | generateAnchor: function(el) { 40 | if (el.id) { 41 | return el.id; 42 | } else { 43 | var anchor = this.generateUniqueId(el); 44 | el.id = anchor; 45 | return anchor; 46 | } 47 | }, 48 | 49 | createNavList: function() { 50 | return $(''); 51 | }, 52 | 53 | createChildNavList: function($parent) { 54 | var $childList = this.createNavList(); 55 | $parent.append($childList); 56 | return $childList; 57 | }, 58 | 59 | generateNavEl: function(anchor, text) { 60 | var $a = $(''); 61 | $a.attr('href', '#' + anchor); 62 | $a.text(text); 63 | var $li = $('
  • '); 64 | $li.append($a); 65 | return $li; 66 | }, 67 | 68 | generateNavItem: function(headingEl) { 69 | var anchor = this.generateAnchor(headingEl); 70 | var $heading = $(headingEl); 71 | var text = $heading.data('toc-text') || $heading.text(); 72 | return this.generateNavEl(anchor, text); 73 | }, 74 | 75 | // Find the first heading level (`

    `, then `

    `, etc.) that has more than one element. Defaults to 1 (for `

    `). 76 | getTopLevel: function($scope) { 77 | for (var i = 1; i <= 6; i++) { 78 | var $headings = this.findOrFilter($scope, 'h' + i); 79 | if ($headings.length > 1) { 80 | return i; 81 | } 82 | } 83 | 84 | return 1; 85 | }, 86 | 87 | // returns the elements for the top level, and the next below it 88 | getHeadings: function($scope, topLevel) { 89 | var topSelector = 'h' + topLevel; 90 | 91 | var secondaryLevel = topLevel + 1; 92 | var secondarySelector = 'h' + secondaryLevel; 93 | 94 | return this.findOrFilter($scope, topSelector + ',' + secondarySelector); 95 | }, 96 | 97 | getNavLevel: function(el) { 98 | return parseInt(el.tagName.charAt(1), 10); 99 | }, 100 | 101 | populateNav: function($topContext, topLevel, $headings) { 102 | var $context = $topContext; 103 | var $prevNav; 104 | 105 | var helpers = this; 106 | $headings.each(function(i, el) { 107 | var $newNav = helpers.generateNavItem(el); 108 | var navLevel = helpers.getNavLevel(el); 109 | 110 | // determine the proper $context 111 | if (navLevel === topLevel) { 112 | // use top level 113 | $context = $topContext; 114 | } else if ($prevNav && $context === $topContext) { 115 | // create a new level of the tree and switch to it 116 | $context = helpers.createChildNavList($prevNav); 117 | } // else use the current $context 118 | 119 | $context.append($newNav); 120 | 121 | $prevNav = $newNav; 122 | }); 123 | }, 124 | 125 | parseOps: function(arg) { 126 | var opts; 127 | if (arg.jquery) { 128 | opts = { 129 | $nav: arg 130 | }; 131 | } else { 132 | opts = arg; 133 | } 134 | opts.$scope = opts.$scope || $(document.body); 135 | return opts; 136 | } 137 | }, 138 | 139 | // accepts a jQuery object, or an options object 140 | init: function(opts) { 141 | opts = this.helpers.parseOps(opts); 142 | 143 | // ensure that the data attribute is in place for styling 144 | opts.$nav.attr('data-toggle', 'toc'); 145 | 146 | var $topContext = this.helpers.createChildNavList(opts.$nav); 147 | var topLevel = this.helpers.getTopLevel(opts.$scope); 148 | var $headings = this.helpers.getHeadings(opts.$scope, topLevel); 149 | this.helpers.populateNav($topContext, topLevel, $headings); 150 | } 151 | }; 152 | 153 | $(function() { 154 | $('nav[data-toggle="toc"]').each(function(i, el) { 155 | var $nav = $(el); 156 | Toc.init($nav); 157 | }); 158 | }); 159 | })(); 160 | -------------------------------------------------------------------------------- /docs/LICENSE-text.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | License • shadow 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
    62 |
    63 | 105 | 106 | 107 | 108 |
    109 | 110 |
    111 |
    112 | 115 | 116 |
    YEAR: 2020
    117 | COPYRIGHT HOLDER: Michael Dorman
    118 | 
    119 | 120 |
    121 | 122 | 127 | 128 |
    129 | 130 | 131 | 132 |
    133 | 136 | 137 |
    138 |

    Site built with pkgdown 1.6.1.

    139 |
    140 | 141 |
    142 |
    143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Page not found (404) • shadow 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
    62 |
    63 | 105 | 106 | 107 | 108 |
    109 | 110 |
    111 |
    112 | 115 | 116 | Content not found. Please use links in the navbar. 117 | 118 |
    119 | 120 | 125 | 126 |
    127 | 128 | 129 | 130 |
    131 | 134 | 135 |
    136 |

    Site built with pkgdown 1.6.1.

    137 |
    138 | 139 |
    140 |
    141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /R/SVFPnt.R: -------------------------------------------------------------------------------- 1 | .SVFPnt = function( 2 | location, 3 | obstacles, 4 | obstacles_height_field, 5 | res_angle = 5, 6 | b = 0.01 7 | ) { 8 | 9 | # Create rays 10 | angles = seq(0, 359.9999, res_angle) 11 | sun = mapply( 12 | .sunLocation, 13 | sun_az = angles, 14 | MoreArgs = list( 15 | location = location, 16 | sun_elev = 0 17 | ) 18 | ) 19 | rays = mapply(shadow::ray, MoreArgs = list(from = location), to = sun) 20 | rays$makeUniqueIDs = TRUE 21 | rays = do.call(sp::rbind.SpatialLines, rays) 22 | 23 | # # Add 'surface' outline to obstacles 24 | # surface = rgeos::gBuffer(location, width = 1000) 25 | # surface = SpatialPolygonsDataFrame( 26 | # Sr = surface, 27 | # data = data.frame(height = 0), 28 | # match.ID = FALSE 29 | # ) 30 | # names(surface) = obstacles_height_field 31 | # surface = rbind(obstacles[, obstacles_height_field], surface) 32 | 33 | # 'obstacles' / 'surface' outline to 'lines' *** DEPENDS ON PACKAGE 'sp' *** 34 | # surface_outline = as(surface, "SpatialLinesDataFrame") 35 | obstacles_outline = as(obstacles, "SpatialLinesDataFrame") 36 | 37 | if( 38 | 39 | # 2D mode - if point is *on* a building then 'SVF=NA' 40 | ( 41 | dimensions(location) == 2 & # 2D 42 | # rgeos::gIntersects(location, obstacles) # Intersects with 'obstacles' 43 | any(unlist(sf::st_intersects(sf::st_as_sf(location), sf::st_as_sf(obstacles)))) 44 | ) || 45 | 46 | # 3D mode - if point is *inside* building then 'SVF=NA' 47 | dimensions(location) == 3 & ( # 3D 48 | # rgeos::gIntersects(location, obstacles) && # Intersects with 'obstacles' 49 | any(unlist(sf::st_intersects(sf::st_as_sf(location), sf::st_as_sf(obstacles)))) && 50 | coordinates(location)[, 3] < obstacles[location, ]@data[, obstacles_height_field] # Location z < Obstacle h 51 | ) 52 | ) svf_final = NA else { 53 | 54 | svf = rep(NA, length(rays)) 55 | 56 | for(i in 1:length(rays)) { 57 | 58 | # 'Line of sight' between sun and location 59 | ray1 = rays[i, ] 60 | 61 | # if(dimensions(location) == 2) { 62 | 63 | # 2D mode - Intersections with 'obstacles' outline 64 | # inter = rgeos::gIntersection(obstacles_outline, ray1) 65 | inter = sf::st_intersection(sf::st_geometry(sf::st_as_sf(obstacles_outline)), sf::st_geometry(sf::st_as_sf(ray1))) 66 | # } 67 | 68 | # if(dimensions(location) == 3) { 69 | # 70 | # # 3D mode - Intersections with 'surface' outline 71 | # inter = rgeos::gIntersection(surface_outline, ray1) 72 | # 73 | # } 74 | 75 | # No intersections means 'SVF=1' 76 | if(length(inter) == 0L) svf[i] = 1 else { 77 | 78 | # If some of the intersections are lines then convert to points 79 | # if(is(inter, "SpatialCollections")) { 80 | # lin = inter@lineobj 81 | # inter = inter@pointobj 82 | # for(lin_i in 1:length(lin)) { 83 | # lin_pnt = lin[lin_i, ] 84 | # lin_pnt = coordinates(lin_pnt)[[1]][[1]] 85 | # lin_pnt = sp::SpatialPoints( 86 | # lin_pnt, 87 | # proj4string = CRS(proj4string(inter)) 88 | # ) 89 | # inter = sp::rbind.SpatialPoints(inter, lin_pnt) 90 | # } 91 | if(length(grep("GEOMETRY", sf::st_geometry_type(inter))) > 0) { 92 | 93 | lin = sf::st_cast(sf::st_collection_extract(inter, "LINESTRING"), "POINT") 94 | inter = sf::st_collection_extract(inter, "POINT") 95 | 96 | inter = as(c(st_geometry(inter), st_geometry(lin)), "Spatial") 97 | } 98 | 99 | # Set row names 100 | 101 | inter = as(as(inter, "Spatial"), "SpatialPoints") 102 | row.names(inter) = 1:length(inter) 103 | 104 | # Extract 'obstacles' / 'surface' data for each intersection 105 | # if(dimensions(location) == 2) outline = obstacles_outline 106 | # if(dimensions(location) == 3) outline = surface_outline 107 | inter = 108 | SpatialPointsDataFrame( 109 | inter, 110 | sp::over( 111 | inter, 112 | # rgeos::gBuffer(obstacles_outline, byid = TRUE, width = b), 113 | as(sf::st_buffer(sf::st_as_sf(obstacles_outline), dist = b), 114 | "Spatial"), 115 | fn = max) 116 | ) 117 | 118 | # Distance between examined location and intersections 119 | # inter$dist = rgeos::gDistance(inter, location, byid = TRUE)[1, ] 120 | inter$dist = sf::st_distance(sf::st_as_sf(inter), sf::st_as_sf(location)) 121 | 122 | # Maximal angle of obstruction calculation 123 | 124 | # 2D mode - height difference equal to obstacles height 125 | if(dimensions(location) == 2) { 126 | inter$height_diff = inter@data[, obstacles_height_field] 127 | } 128 | 129 | # 3D mode - location height *subtracted* from obstacles height 130 | if(dimensions(location) == 3) { 131 | inter$height_diff = max( 132 | inter@data[, obstacles_height_field] - coordinates(location)[, 3], 133 | 0 # If location z > obstacles h then 'height_diff=0' 134 | ) 135 | } 136 | 137 | inter$angle = rad2deg(atan(inter$height_diff / inter$dist)) 138 | # inter$svf = 1 - inter$angle / 90 139 | inter$svf = 1 - sin(deg2rad(inter$angle))^2 # Gal & Unger 2014 140 | svf[i] = min(inter$svf) 141 | 142 | } 143 | 144 | } 145 | 146 | svf_final = mean(svf) 147 | 148 | } 149 | 150 | return(svf_final) 151 | 152 | } 153 | -------------------------------------------------------------------------------- /R/data.R: -------------------------------------------------------------------------------- 1 | #' Polygonal layer of four buildings in Rishon 2 | #' 3 | #' A \code{SpatialPolygonsDataFrame} object representing the outlines of four buildings located in Rishon-Le-Zion. The attribute \code{BLDG_HT} contains building height, in meters. 4 | #' 5 | #' @format A \code{SpatialPolygonsDataFrame} with 4 features and 2 attributes: 6 | #' \describe{ 7 | #' \item{build_id}{Building ID} 8 | #' \item{BLDG_HT}{Building height, in meters} 9 | #' } 10 | #' @examples 11 | #' build 12 | #' plot(build, axes = TRUE) 13 | 14 | "build" 15 | 16 | #' Polygonal layer of three buildings in Boston 17 | #' 18 | #' A \code{SpatialPolygonsDataFrame} object representing the outlines of three buildings located in Central Boston. The attribute \code{height_m} contains building height, in meters. 19 | #' 20 | #' @format A \code{SpatialPolygonsDataFrame} with 10 features and 4 attributes: 21 | #' \describe{ 22 | #' \item{objectid}{Building part ID} 23 | #' \item{build_id}{Building ID} 24 | #' \item{part_floor}{Number of floors for part} 25 | #' \item{height_m}{Building height, in meters} 26 | #' } 27 | #' @examples 28 | #' boston_build 29 | #' plot(boston_build, axes = TRUE) 30 | 31 | "boston_build" 32 | 33 | #' Polygonal layer of 376 buildings in Beer-Sheva 34 | #' 35 | #' A \code{SpatialPolygonsDataFrame} object representing the outlines of 367 buildings in the Ramot neighborhood, Beer-Sheva. The attribute \code{height_m} contains building height, in meters. 36 | #' 37 | #' @format A \code{SpatialPolygonsDataFrame} with 10 features and 4 attributes: 38 | #' \describe{ 39 | #' \item{build_id}{Building ID} 40 | #' \item{floors}{Number of floors for building} 41 | #' \item{apartments}{Number of apartments} 42 | #' \item{height_m}{Building height, in meters} 43 | #' \item{elev}{Elevation above sea level of building base, in meters} 44 | #' } 45 | #' @examples 46 | #' beersheva_build 47 | #' plot(beersheva_build, axes = TRUE) 48 | 49 | "beersheva_build" 50 | 51 | #' Polygonal layer of a building block in Boston 52 | #' 53 | #' A \code{SpatialPolygons} object representing the boundaries of a building block in Central Boston. 54 | #' 55 | #' @format A \code{SpatialPolygons} with a single feature. 56 | #' @examples 57 | #' boston_block 58 | #' plot(boston_block, axes = TRUE) 59 | 60 | "boston_block" 61 | 62 | #' Polygonal layer of a park in Boston 63 | #' 64 | #' A \code{SpatialPolygons} object representing the boundaries of a park in Central Boston. 65 | #' 66 | #' @format A \code{SpatialPolygons} with a single feature. 67 | #' @examples 68 | #' boston_park 69 | #' plot(boston_park, axes = TRUE) 70 | 71 | "boston_park" 72 | 73 | #' Polygonal layer of sidewalks in Boston 74 | #' 75 | #' A \code{SpatialLinesDataFrame} object representing sidewalks in Central Boston. 76 | #' 77 | #' @format A \code{SpatialLinesDataFrame} with 78 features. 78 | #' @examples 79 | #' boston_sidewalk 80 | #' plot(boston_sidewalk, axes = TRUE) 81 | 82 | "boston_sidewalk" 83 | 84 | #' Typical Meteorological Year (TMY) solar radiation in Tel-Aviv 85 | #' 86 | #' A table with hourly solar radiation estimates for a typical meteorological year in Tel-Aviv. \itemize{ 87 | #' \item{\code{time} Time, as \code{character} in the \code{"\%Y-\%m-\%d \%H:\%M:\%S"} format, e.g. \code{"2000-01-01 06:00:00"}}, referring to local time 88 | #' \item{\code{sun_az} Sun azimuth, in decimal degrees from North} 89 | #' \item{\code{sun_elev} Sun elevation, in decimal degrees} 90 | #' \item{\code{solar_normal} Direct Normal Irradiance, in Wh/m^2} 91 | #' \item{\code{solar_diffuse} Diffuse Horizontal Irradiance, in Wh/m^2} 92 | #' \item{\code{dbt} Dry-bulb temperature, in Celsius degrees} 93 | #' \item{\code{ws} Wind speed, in m/s} 94 | #' } 95 | #' 96 | #' @format A \code{data.frame} with 8760 rows and 7 columns. 97 | #' 98 | #' @references 99 | #' \url{https://energyplus.net/weather-location/europe_wmo_region_6/ISR/ISR_Tel.Aviv-Bet.Dagan.401790_MSI} 100 | #' @examples 101 | #' head(tmy) 102 | 103 | "tmy" 104 | 105 | #' Typical Meteorological Year (TMY) solar radiation in Beer-Sheva 106 | #' 107 | #' A table with hourly solar radiation estimates for a typical meteorological year in Beer-Sheva. \itemize{ 108 | #' \item{\code{time} Time, as \code{character} in the \code{"\%Y-\%m-\%d \%H:\%M:\%S"} format, e.g. \code{"2000-01-01 06:00:00"}}, referring to local time 109 | #' \item{\code{sun_az} Sun azimuth, in decimal degrees from North} 110 | #' \item{\code{sun_elev} Sun elevation, in decimal degrees} 111 | #' \item{\code{solar_normal} Direct Normal Irradiance, in Wh/m^2} 112 | #' \item{\code{solar_diffuse} Diffuse Horizontal Irradiance, in Wh/m^2} 113 | #' \item{\code{dbt} Dry-bulb temperature, in Celsius degrees} 114 | #' \item{\code{ws} Wind speed, in m/s} 115 | #' } 116 | #' 117 | #' @format A \code{data.frame} with 8760 rows and 7 columns. 118 | #' 119 | #' @references 120 | #' \url{https://energyplus.net/weather-location/europe_wmo_region_6/ISR/ISR_Beer.Sheva.401900_MSI} 121 | #' @examples 122 | #' head(tmy2) 123 | 124 | "tmy2" 125 | 126 | #' DEM of Ramot neighborhood, Beer-Sheva 127 | #' 128 | #' Digital Elevation Model (DEM) of Ramot neighborhood, Beer-Sheva. Raster values represent elevation above sea level, in meters. 129 | #' 130 | #' @format A \code{RasterLayer} representing a grid of 1974 raster cells, each cell is a 30*30 meters rectangle. Data source is the Shuttle Radar Topography Mission (SRTM) 1 Arc-Second Global dataset. 131 | #' 132 | #' @references 133 | #' \url{https://www.usgs.gov/centers/eros/science/usgs-eros-archive-digital-elevation-shuttle-radar-topography-mission-srtm-1} 134 | #' @examples 135 | #' beersheva_elev 136 | #' plot(beersheva_elev) 137 | 138 | "beersheva_elev" 139 | 140 | -------------------------------------------------------------------------------- /docs/authors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Authors • shadow 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
    62 |
    63 | 105 | 106 | 107 | 108 |
    109 | 110 |
    111 |
    112 | 115 | 116 |
      117 |
    • 118 |

      Michael Dorman. Author, maintainer. 119 |

      120 |
    • 121 |
    • 122 |

      Evyatar Erell. Contributor. 123 |

      124 |
    • 125 |
    • 126 |

      Itai Kloog. Contributor. 127 |

      128 |
    • 129 |
    • 130 |

      Adi Vulkan. Contributor. 131 |

      132 |
    • 133 |
    • 134 |

      Roger Bivand. Contributor. 135 |

      136 |
    • 137 |
    138 | 139 |
    140 | 141 |
    142 | 143 | 144 | 145 |
    146 | 149 | 150 |
    151 |

    Site built with pkgdown 1.6.1.

    152 |
    153 | 154 |
    155 |
    156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /R/shadowFootprint.R: -------------------------------------------------------------------------------- 1 | #' Shadow footprint on the ground 2 | #' 3 | #' Creates a polygonal layer of shadow footprints on the ground, taking into account:\itemize{ 4 | #' \item{Obstacles outline (\code{obstacles}), given by a polygonal layer with a height attribute (\code{obstacles_height_field})} 5 | #' \item{Sun position (\code{solar_pos}), given by azimuth and elevation angles} 6 | #' } 7 | #' The calculation method was inspired by Morel Weisthal's MSc thesis at the Ben-Gurion University of the Negev. 8 | #' 9 | #' @param obstacles A \code{SpatialPolygonsDataFrame} object specifying the obstacles outline 10 | #' @param obstacles_height_field Name of attribute in \code{obstacles} with extrusion height for each feature 11 | #' @param solar_pos A \code{matrix} with one row and two columns; first column is the solar azimuth (in decimal degrees from North), second column is sun elevation (in decimal degrees) 12 | #' @param time When \code{solar_pos} is unspecified, \code{time} can be passed to automatically calculate \code{solar_pos} based on the time and the centroid of \code{obstacles}, using function \code{suntools::solarpos}. In such case \code{obstacles} must have a defined CRS (not \code{NA}). The \code{time} value must be a \code{POSIXct} or \code{POSIXlt} object 13 | 14 | #' @param b Buffer size for shadow footprints of individual segments of a given polygon; used to eliminate minor internal holes in the resulting shadow polygon. 15 | #' 16 | #' @return A \code{SpatialPolygonsDataFrame} object representing shadow footprint, plus buildings outline. Object length is the same as that of the input \code{obstacles}, with an individual footprint feature for each obstacle. 17 | #' 18 | #' @references 19 | #' Weisthal, M. (2014). Assessment of potential energy savings in Israel through climate-aware residential building design (MSc Thesis, Ben-Gurion University of the Negev). 20 | #' \url{https://www.dropbox.com/s/bztnh1fi9znmswj/Thesis_Morel_Weisthal.pdf?dl=1} 21 | #' 22 | #' @examples 23 | #' time = as.POSIXct("2004-12-24 13:30:00", tz = "Asia/Jerusalem") 24 | #' proj4string(build) = CRS("EPSG:32636") 25 | #' location_geo = matrix(c(34.7767978098526, 31.9665936050395), ncol = 2) 26 | #' solar_pos = suntools::solarpos(location_geo, time) 27 | #' footprint1 = ## Using 'solar_pos' 28 | #' shadowFootprint( 29 | #' obstacles = build, 30 | #' obstacles_height_field = "BLDG_HT", 31 | #' solar_pos = solar_pos 32 | #' ) 33 | #' footprint2 = ## Using 'time' 34 | #' shadowFootprint( 35 | #' obstacles = build, 36 | #' obstacles_height_field = "BLDG_HT", 37 | #' time = time 38 | #' ) 39 | #' all.equal(footprint1, footprint2) 40 | #' footprint = footprint1 41 | #' plot(footprint, col = adjustcolor("lightgrey", alpha.f = 0.5)) 42 | #' plot(build, add = TRUE, col = "darkgrey") 43 | #' 44 | #' @export 45 | #' @importFrom sf st_buffer 46 | #' @importFrom sf st_as_sf 47 | #' @importFrom sf st_length 48 | #' @importFrom sf st_convex_hull 49 | #' @importFrom sf st_union 50 | #' @importFrom sf st_geometry 51 | #' @importFrom methods as 52 | #' @name shadowFootprint 53 | 54 | NULL 55 | 56 | setGeneric("shadowFootprint", function( 57 | obstacles, 58 | obstacles_height_field, 59 | ... 60 | ) { 61 | standardGeneric("shadowFootprint") 62 | }) 63 | 64 | #' @rdname shadowFootprint 65 | 66 | setMethod( 67 | 68 | f = "shadowFootprint", 69 | 70 | signature = c( 71 | obstacles = "SpatialPolygonsDataFrame" 72 | ), 73 | 74 | function( 75 | obstacles, 76 | obstacles_height_field, 77 | solar_pos = solarpos2(obstacles, time), 78 | time = NULL, 79 | b = 0.01 80 | ) { 81 | 82 | # Check inputs 83 | .checkSolarPos(solar_pos, length1 = TRUE) 84 | .checkObstacles(obstacles, obstacles_height_field) 85 | 86 | # Container for footprints of individual 'obstacles' features 87 | footprint_final = list() 88 | 89 | for(i in 1:length(obstacles)) { 90 | 91 | # Calculate shift distance 92 | dist = obstacles@data[i, obstacles_height_field] / tan(deg2rad(solar_pos[1,2])) 93 | 94 | # Polygon to line segments 95 | seg = shadow::toSeg(obstacles[i, ]) 96 | 97 | # Discard zero-length segments 98 | # seg = seg[rgeos::gLength(seg, byid = TRUE) > 0, ] 99 | seg = seg[unclass(sf::st_length(sf::st_as_sf(seg))) > 0, ] 100 | 101 | # Shift segments 102 | seg_shifted = shadow::shiftAz(seg, az = solar_pos[1, 1], dist = -dist) 103 | 104 | # Container for footprints of individual segments of one 'obstacles' feature 105 | footprint = list() 106 | 107 | for(j in 1:length(seg)) { 108 | 109 | f = sp::rbind.SpatialLines(seg[j, ], seg_shifted[j, ], makeUniqueIDs = TRUE) 110 | # f = rgeos::gConvexHull(f) 111 | f = as(sf::st_convex_hull(sf::st_geometry(sf::st_union(sf::st_as_sf(f)))), "Spatial") 112 | footprint[[j]] = f 113 | 114 | } 115 | 116 | # Bind footprings of individual segments of one 'obstacles' feature 117 | footprint$makeUniqueIDs = TRUE 118 | footprint = do.call(sp::rbind.SpatialPolygons, footprint) 119 | # footprint = rgeos::gUnaryUnion(footprint) 120 | # footprint = rgeos::gBuffer(footprint, width = b) 121 | # footprint_final[[i]] = rgeos::gUnion(footprint, obstacles[i, ]) 122 | footprint = sf::st_union(sf::st_geometry(sf::st_as_sf(footprint))) 123 | footprint = sf::st_buffer(footprint, dist = b) 124 | footprint_final[[i]] = as(sf::st_union(sf::st_geometry(footprint)), "Spatial") 125 | 126 | } 127 | 128 | # Bind footprints of individual 'obstacles' features 129 | footprint_final$makeUniqueIDs = TRUE 130 | footprint_final = do.call(sp::rbind.SpatialPolygons, footprint_final) 131 | footprint_final = SpatialPolygonsDataFrame(footprint_final, obstacles@data, match.ID = FALSE) 132 | 133 | return(footprint_final) 134 | 135 | } 136 | ) 137 | 138 | -------------------------------------------------------------------------------- /man/SVF.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/SVF.R 3 | \name{SVF} 4 | \alias{SVF} 5 | \alias{SVF,SpatialPoints-method} 6 | \alias{SVF,Raster-method} 7 | \title{Sky View Factor (SVF) calculation} 8 | \usage{ 9 | \S4method{SVF}{SpatialPoints}( 10 | location, 11 | obstacles, 12 | obstacles_height_field, 13 | res_angle = 5, 14 | b = 0.01, 15 | parallel = getOption("mc.cores") 16 | ) 17 | 18 | \S4method{SVF}{Raster}( 19 | location, 20 | obstacles, 21 | obstacles_height_field, 22 | res_angle = 5, 23 | b = 0.01, 24 | parallel = getOption("mc.cores") 25 | ) 26 | } 27 | \arguments{ 28 | \item{location}{A \code{SpatialPoints*} or \code{Raster*} object, specifying the location(s) for which to calculate logical shadow values. If \code{location} is \code{SpatialPoints*}, then it can have 2 or 3 dimensions. A 2D \code{SpatialPoints*} is considered as a point(s) on the ground, i.e. 3D point(s) where \eqn{z=0}. In a 3D \code{SpatialPoints*} the 3rd dimension is assumed to be elevation above ground \eqn{z} (in CRS units). \code{Raster*} cells are considered as ground locations} 29 | 30 | \item{obstacles}{A \code{SpatialPolygonsDataFrame} object specifying the obstacles outline} 31 | 32 | \item{obstacles_height_field}{Name of attribute in \code{obstacles} with extrusion height for each feature} 33 | 34 | \item{res_angle}{Circular sampling resolution, in decimal degrees. Default is 5 degrees, i.e. 0, 5, 10... 355.} 35 | 36 | \item{b}{Buffer size when joining intersection points with building outlines, to determine intersection height} 37 | 38 | \item{parallel}{Number of parallel processes or a predefined socket cluster. With \code{parallel=1} uses ordinary, non-parallel processing. Parallel processing is done with the \code{parallel} package} 39 | } 40 | \value{ 41 | A numeric value between 0 (sky completely obstructed) and 1 (sky completely visible). 42 | \itemize{ 43 | \item{If input \code{location} is a \code{SpatialPoints*}, then returned object is a \code{vector} where each element representing the SVF for each point in \code{location}} 44 | \item{If input \code{location} is a \code{Raster*}, then returned object is a \code{RasterLayer} where cell values express SVF for each ground location} 45 | } 46 | } 47 | \description{ 48 | Calculates the Sky View Factor (SVF) at given points or complete grid (\code{location}), taking into account obstacles outline (\code{obstacles}) given by a polygonal layer with a height attribute (\code{obstacles_height_field}). 49 | } 50 | \note{ 51 | SVF calculation for each view direction follows the following equation - 52 | \deqn{1 - (sin(\beta))^2} 53 | Where \eqn{\beta} is the highest elevation angle (see equation 3 in Gal & Unger 2014). 54 | } 55 | \examples{ 56 | ## Individual locations 57 | # location0 = rgeos::gCentroid(build) 58 | location0 = as(sf::st_centroid(sf::st_union(sf::st_geometry(sf::st_as_sf(build)))), "Spatial") 59 | location1 = raster::shift(location0, 0, -15) 60 | location2 = raster::shift(location0, -10, 20) 61 | locations = rbind(location1, location2) 62 | svfs = SVF( 63 | location = locations, 64 | obstacles = build, 65 | obstacles_height_field = "BLDG_HT" 66 | ) 67 | plot(build) 68 | plot(locations, add = TRUE) 69 | raster::text(locations, round(svfs, 2), col = "red", pos = 3) 70 | 71 | \dontrun{ 72 | 73 | ## Grid 74 | ext = as(raster::extent(build), "SpatialPolygons") 75 | r = raster::raster(ext, res = 5) 76 | proj4string(r) = proj4string(build) 77 | pnt = raster::rasterToPoints(r, spatial = TRUE) 78 | svfs = SVF( 79 | location = r, 80 | obstacles = build, 81 | obstacles_height_field = "BLDG_HT", 82 | parallel = 3 83 | ) 84 | plot(svfs, col = grey(seq(0.9, 0.2, -0.01))) 85 | raster::contour(svfs, add = TRUE) 86 | plot(build, add = TRUE, border = "red") 87 | 88 | ## 3D points 89 | # ctr = rgeos::gCentroid(build) 90 | ctr = as(sf::st_centroid(sf::st_union(sf::st_geometry(sf::st_as_sf(build)))), "Spatial") 91 | heights = seq(0, 28, 1) 92 | loc3d = data.frame( 93 | x = coordinates(ctr)[, 1], 94 | y = coordinates(ctr)[, 2], 95 | z = heights 96 | ) 97 | coordinates(loc3d) = ~ x + y + z 98 | proj4string(loc3d) = proj4string(build) 99 | svfs = SVF( 100 | location = loc3d, 101 | obstacles = build, 102 | obstacles_height_field = "BLDG_HT", 103 | parallel = 3 104 | ) 105 | plot(heights, svfs, type = "b", xlab = "Elevation (m)", ylab = "SVF", ylim = c(0, 1)) 106 | abline(v = build$BLDG_HT, col = "red") 107 | 108 | ## Example from Erell et al. 2012 (p. 19 Fig. 1.2) 109 | 110 | # Geometry 111 | # pol1 = rgeos::readWKT("POLYGON ((0 100, 1 100, 1 0, 0 0, 0 100))") 112 | pol1 = as(sf::st_as_sfc("POLYGON ((0 100, 1 100, 1 0, 0 0, 0 100))"), "Spatial") 113 | # pol2 = rgeos::readWKT("POLYGON ((2 100, 3 100, 3 0, 2 0, 2 100))") 114 | pol2 = as(sf::st_as_sfc("POLYGON ((2 100, 3 100, 3 0, 2 0, 2 100))"), "Spatial") 115 | pol = sp::rbind.SpatialPolygons(pol1, pol2, makeUniqueIDs = TRUE) 116 | pol = sp::SpatialPolygonsDataFrame(pol, data.frame(h = c(1, 1)), match.ID = FALSE) 117 | # pnt = rgeos::readWKT("POINT (1.5 50)") 118 | pnt = as(sf::st_as_sfc("POINT (1.5 50)"), "Spatial") 119 | plot(pol, col = "grey", xlim = c(0, 3), ylim = c(45, 55)) 120 | plot(pnt, add = TRUE, col = "red") 121 | 122 | # Fig. 1.2 reproduction 123 | h = seq(0, 2, 0.1) 124 | svf = rep(NA, length(h)) 125 | for(i in 1:length(h)) { 126 | pol$h = h[i] 127 | svf[i] = SVF(location = pnt, obstacles = pol, obstacles_height_field = "h", res_angle = 1) 128 | } 129 | plot(h, svf, type = "b", ylim = c(0, 1)) 130 | 131 | # Comparison with SVF values from the book 132 | test = c(1, 0.9805806757, 0.9284766909, 0.8574929257, 0.7808688094, 133 | 0.7071067812, 0.6401843997, 0.5812381937, 0.52999894, 0.4856429312, 134 | 0.4472135955, 0.4138029443, 0.3846153846, 0.3589790793, 0.336336397, 135 | 0.316227766, 0.2982749931, 0.282166324, 0.2676438638, 0.2544932993, 136 | 0.242535625) 137 | range(test - svf) 138 | 139 | } 140 | 141 | } 142 | \references{ 143 | Erell, E., Pearlmutter, D., & Williamson, T. (2012). Urban microclimate: designing the spaces between buildings. Routledge. 144 | 145 | Gal, T., & Unger, J. (2014). A new software tool for SVF calculations using building and tree-crown databases. Urban Climate, 10, 594-606. 146 | } 147 | -------------------------------------------------------------------------------- /docs/reference/deg2rad.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Degrees to radians — deg2rad • shadow 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 106 | 107 | 108 | 109 |
    110 | 111 |
    112 |
    113 | 118 | 119 |
    120 |

    Degrees to radians

    121 |
    122 | 123 |
    deg2rad(deg)
    124 | 125 |

    Arguments

    126 | 127 | 128 | 129 | 130 | 131 | 132 |
    deg

    Angle in degrees

    133 | 134 |

    Value

    135 | 136 |

    numeric Angle in radians

    137 | 138 |

    Examples

    139 |
    deg2rad(360) == 2*pi 140 |
    #> [1] TRUE
    141 |
    142 | 147 |
    148 | 149 | 150 |
    151 | 154 | 155 |
    156 |

    Site built with pkgdown 1.6.1.

    157 |
    158 | 159 |
    160 |
    161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /docs/reference/rad2deg.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Radians to degrees — rad2deg • shadow 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 106 | 107 | 108 | 109 |
    110 | 111 |
    112 |
    113 | 118 | 119 |
    120 |

    Radians to degrees

    121 |
    122 | 123 |
    rad2deg(rad)
    124 | 125 |

    Arguments

    126 | 127 | 128 | 129 | 130 | 131 | 132 |
    rad

    Angle in radians

    133 | 134 |

    Value

    135 | 136 |

    numeric Angle in degrees

    137 | 138 |

    Examples

    139 |
    rad2deg(2*pi) == 360 140 |
    #> [1] TRUE
    141 |
    142 | 147 |
    148 | 149 | 150 |
    151 | 154 | 155 |
    156 |

    Site built with pkgdown 1.6.1.

    157 |
    158 | 159 |
    160 |
    161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /docs/reference/boston_block.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Polygonal layer of a building block in Boston — boston_block • shadow 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 106 | 107 | 108 | 109 |
    110 | 111 |
    112 |
    113 | 118 | 119 |
    120 |

    A SpatialPolygons object representing the boundaries of a building block in Central Boston.

    121 |
    122 | 123 |
    boston_block
    124 | 125 | 126 |

    Format

    127 | 128 |

    A SpatialPolygons with a single feature.

    129 | 130 |

    Examples

    131 |
    boston_block 132 |
    #> class : SpatialPolygons 133 | #> features : 1 134 | #> extent : 328903.9, 329144.9, 4690539, 4690799 (xmin, xmax, ymin, ymax) 135 | #> crs : +proj=utm +zone=19 +datum=WGS84 +units=m +no_defs
    plot(boston_block, axes = TRUE) 136 |
    137 |
    138 | 143 |
    144 | 145 | 146 |
    147 | 150 | 151 |
    152 |

    Site built with pkgdown 1.6.1.

    153 |
    154 | 155 |
    156 |
    157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /docs/reference/boston_park.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Polygonal layer of a park in Boston — boston_park • shadow 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 106 | 107 | 108 | 109 |
    110 | 111 |
    112 |
    113 | 118 | 119 |
    120 |

    A SpatialPolygons object representing the boundaries of a park in Central Boston.

    121 |
    122 | 123 |
    boston_park
    124 | 125 | 126 |

    Format

    127 | 128 |

    A SpatialPolygons with a single feature.

    129 | 130 |

    Examples

    131 |
    boston_park 132 |
    #> class : SpatialPolygonsDataFrame 133 | #> features : 1 134 | #> extent : 328920.9, 329072.8, 4690670, 4690782 (xmin, xmax, ymin, ymax) 135 | #> crs : +proj=utm +zone=19 +datum=WGS84 +units=m +no_defs 136 | #> variables : 1 137 | #> names : objectid 138 | #> value : 46
    plot(boston_park, axes = TRUE) 139 |
    140 |
    141 | 146 |
    147 | 148 | 149 |
    150 | 153 | 154 |
    155 |

    Site built with pkgdown 1.6.1.

    156 |
    157 | 158 |
    159 |
    160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /man/shadowHeight.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/shadowHeight.R 3 | \name{shadowHeight} 4 | \alias{shadowHeight} 5 | \alias{shadowHeight,SpatialPoints-method} 6 | \alias{shadowHeight,Raster-method} 7 | \title{Shadow height calculation considering sun position and obstacles} 8 | \usage{ 9 | \S4method{shadowHeight}{SpatialPoints}( 10 | location, 11 | obstacles, 12 | obstacles_height_field, 13 | solar_pos = solarpos2(location, time), 14 | time = NULL, 15 | b = 0.01, 16 | parallel = getOption("mc.cores"), 17 | filter_footprint = FALSE 18 | ) 19 | 20 | \S4method{shadowHeight}{Raster}( 21 | location, 22 | obstacles, 23 | obstacles_height_field, 24 | solar_pos = solarpos2(pnt, time), 25 | time = NULL, 26 | b = 0.01, 27 | parallel = getOption("mc.cores"), 28 | filter_footprint = FALSE 29 | ) 30 | } 31 | \arguments{ 32 | \item{location}{A \code{SpatialPoints*} or \code{Raster*} object, specifying the location(s) for which to calculate shadow height} 33 | 34 | \item{obstacles}{A \code{SpatialPolygonsDataFrame} object specifying the obstacles outline} 35 | 36 | \item{obstacles_height_field}{Name of attribute in \code{obstacles} with extrusion height for each feature} 37 | 38 | \item{solar_pos}{A \code{matrix} with two columns representing sun position(s); first column is the solar azimuth (in decimal degrees from North), second column is sun elevation (in decimal degrees); rows represent different positions (e.g. at different times of day)} 39 | 40 | \item{time}{When \code{solar_pos} is unspecified, \code{time} can be passed to automatically calculate \code{solar_pos} based on the time and the centroid of \code{location}, using function \code{suntools::solarpos}. In such case \code{location} must have a defined CRS (not \code{NA}). The \code{time} value must be a \code{POSIXct} or \code{POSIXlt} object} 41 | 42 | \item{b}{Buffer size when joining intersection points with building outlines, to determine intersection height} 43 | 44 | \item{parallel}{Number of parallel processes or a predefined socket cluster. With \code{parallel=1} uses ordinary, non-parallel processing. Parallel processing is done with the \code{parallel} package} 45 | 46 | \item{filter_footprint}{Should the points be filtered using \code{shadowFootprint} before calculating shadow height? This can make the calculation faster when there are many point which are not shaded} 47 | } 48 | \value{ 49 | Returned object is either a numeric \code{matrix} or a \code{Raster*} - 50 | \itemize{ 51 | \item{If input \code{location} is a \code{SpatialPoints*}, then returned object is a \code{matrix}, where rows represent spatial locations (\code{location} features), columns represent solar positions (\code{solar_pos} rows) and values represent shadow height} 52 | \item{If input \code{location} is a \code{Raster*}, then returned object is a \code{RasterLayer} or \code{RasterStack} where layers represent solar positions (\code{solar_pos} rows) and pixel values represent shadow height} 53 | } 54 | In both cases the numeric values express shadow height - 55 | \itemize{ 56 | \item{\code{NA} value means no shadow} 57 | \item{A \strong{valid number} expresses shadow height, in CRS units (e.g. meters)} 58 | \item{\code{Inf} means complete shadow (i.e. sun below horizon)} 59 | } 60 | } 61 | \description{ 62 | This function calculates shadow height at given points or complete grid (\code{location}), 63 | taking into account:\itemize{ 64 | \item{Obstacles outline (\code{obstacles}), given by a polygonal layer with a height attribute (\code{obstacles_height_field})} 65 | \item{Sun position (\code{solar_pos}), given by azimuth and elevation angles} 66 | } 67 | } 68 | \note{ 69 | For a correct geometric calculation, make sure that:\itemize{ 70 | \item{The layers \code{location} and \code{obstacles} are projected and in same CRS} 71 | \item{The values in \code{obstacles_height_field} of \code{obstacles} are given in the same distance units as the CRS (e.g. meters when using UTM)} 72 | } 73 | } 74 | \examples{ 75 | # Single location 76 | # location = rgeos::gCentroid(build) 77 | location = as(sf::st_centroid(sf::st_union(sf::st_as_sf(build))), "Spatial") 78 | location_geo = matrix(c(34.7767978098526, 31.9665936050395), ncol = 2) 79 | time = as.POSIXct("2004-12-24 13:30:00", tz = "Asia/Jerusalem") 80 | solar_pos = suntools::solarpos(location_geo, time) 81 | plot(build, main = time) 82 | plot(location, add = TRUE) 83 | sun = shadow:::.sunLocation(location = location, sun_az = solar_pos[1,1], sun_elev = solar_pos[1,2]) 84 | sun_ray = ray(from = location, to = sun) 85 | build_outline = as(build, "SpatialLinesDataFrame") 86 | # inter = rgeos::gIntersection(build_outline, sun_ray) 87 | inter = as(sf::st_intersection(sf::st_geometry(sf::st_as_sf(build_outline)), 88 | sf::st_as_sf(sun_ray)), "Spatial") 89 | plot(sun_ray, add = TRUE, col = "yellow") 90 | plot(inter, add = TRUE, col = "red") 91 | shadowHeight( 92 | location = location, 93 | obstacles = build, 94 | obstacles_height_field = "BLDG_HT", 95 | solar_pos = solar_pos 96 | ) 97 | 98 | # Automatically calculating 'solar_pos' using 'time' 99 | proj4string(build) = CRS("EPSG:32636") 100 | proj4string(location) = CRS("EPSG:32636") 101 | shadowHeight( 102 | location = location, 103 | obstacles = build, 104 | obstacles_height_field = "BLDG_HT", 105 | time = time 106 | ) 107 | 108 | \dontrun{ 109 | 110 | # Two points - three times 111 | # location0 = rgeos::gCentroid(build) 112 | location0 = as(sf::st_centroid(sf::st_union(sf::st_as_sf(build))), "Spatial") 113 | location1 = raster::shift(location0, 0, -15) 114 | location2 = raster::shift(location0, -10, 20) 115 | locations = rbind(location1, location2) 116 | time = as.POSIXct("2004-12-24 13:30:00", tz = "Asia/Jerusalem") 117 | times = seq(from = time, by = "1 hour", length.out = 3) 118 | shadowHeight( ## Using 'solar_pos' 119 | location = locations, 120 | obstacles = build, 121 | obstacles_height_field = "BLDG_HT", 122 | solar_pos = suntools::solarpos(location_geo, times) 123 | ) 124 | shadowHeight( ## Using 'time' 125 | location = locations, 126 | obstacles = build, 127 | obstacles_height_field = "BLDG_HT", 128 | time = times 129 | ) 130 | 131 | # Grid - three times 132 | time = as.POSIXct("2004-12-24 13:30:00", tz = "Asia/Jerusalem") 133 | times = seq(from = time, by = "1 hour", length.out = 3) 134 | ext = as(raster::extent(build), "SpatialPolygons") 135 | r = raster::raster(ext, res = 2) 136 | proj4string(r) = proj4string(build) 137 | x = Sys.time() 138 | shadow1 = shadowHeight( 139 | location = r, 140 | obstacles = build, 141 | obstacles_height_field = "BLDG_HT", 142 | time = times, 143 | parallel = 3 144 | ) 145 | y = Sys.time() 146 | y - x 147 | x = Sys.time() 148 | shadow2 = shadowHeight( 149 | location = r, 150 | obstacles = build, 151 | obstacles_height_field = "BLDG_HT", 152 | solar_pos = solarpos2(r, times), 153 | parallel = 3 154 | ) 155 | y = Sys.time() 156 | y - x 157 | shadow = shadow1 158 | opar = par(mfrow = c(1, 3)) 159 | for(i in 1:raster::nlayers(shadow)) { 160 | plot(shadow[[i]], col = grey(seq(0.9, 0.2, -0.01)), main = raster::getZ(shadow)[i]) 161 | raster::contour(shadow[[i]], add = TRUE) 162 | plot(build, border = "red", add = TRUE) 163 | } 164 | par(opar) 165 | 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /docs/reference/toGMT.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Local time to GMT — toGMT • shadow 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 106 | 107 | 108 | 109 |
    110 | 111 |
    112 |
    113 | 118 | 119 |
    120 |

    The function transforms a POSIXct object in any given time zone to GMT.

    121 |
    122 | 123 |
    toGMT(time)
    124 | 125 |

    Arguments

    126 | 127 | 128 | 129 | 130 | 131 | 132 |
    time

    Time, a POSIXct object.

    133 | 134 |

    Value

    135 | 136 |

    A a POSIXct object, in GMT.

    137 | 138 |

    Examples

    139 |
    time = as.POSIXct("1999-01-01 12:00:00", tz = "Asia/Jerusalem") 140 | toGMT(time) 141 |
    #> [1] "1999-01-01 10:00:00 GMT"
    142 |
    143 |
    144 | 149 |
    150 | 151 | 152 |
    153 | 156 | 157 |
    158 |

    Site built with pkgdown 1.6.1.

    159 |
    160 | 161 |
    162 |
    163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /docs/reference/boston_sidewalk.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Polygonal layer of sidewalks in Boston — boston_sidewalk • shadow 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 106 | 107 | 108 | 109 |
    110 | 111 |
    112 |
    113 | 118 | 119 |
    120 |

    A SpatialLinesDataFrame object representing sidewalks in Central Boston.

    121 |
    122 | 123 |
    boston_sidewalk
    124 | 125 | 126 |

    Format

    127 | 128 |

    A SpatialLinesDataFrame with 78 features.

    129 | 130 |

    Examples

    131 |
    boston_sidewalk 132 |
    #> class : SpatialLinesDataFrame 133 | #> features : 78 134 | #> extent : 328900, 329148, 4690530, 4690805 (xmin, xmax, ymin, ymax) 135 | #> crs : +proj=utm +zone=19 +datum=WGS84 +units=m +no_defs 136 | #> variables : 3 137 | #> names : OBJECTID, TYPE, ShapeSTLen 138 | #> min values : 100131, CWALK-CL, 1.60299290715796 139 | #> max values : 99792, SWALK-CL, 314.796524738377
    plot(boston_sidewalk, axes = TRUE) 140 |
    141 |
    142 | 147 |
    148 | 149 | 150 |
    151 | 154 | 155 |
    156 |

    Site built with pkgdown 1.6.1.

    157 |
    158 | 159 |
    160 |
    161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /docs/reference/build.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Polygonal layer of four buildings in Rishon — build • shadow 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 106 | 107 | 108 | 109 |
    110 | 111 |
    112 |
    113 | 118 | 119 |
    120 |

    A SpatialPolygonsDataFrame object representing the outlines of four buildings located in Rishon-Le-Zion. The attribute BLDG_HT contains building height, in meters.

    121 |
    122 | 123 |
    build
    124 | 125 | 126 |

    Format

    127 | 128 |

    A SpatialPolygonsDataFrame with 4 features and 2 attributes:

    129 |
    build_id

    Building ID

    130 |
    BLDG_HT

    Building height, in meters

    131 | 132 |
    133 | 134 | 135 |

    Examples

    136 |
    build 137 |
    #> class : SpatialPolygonsDataFrame 138 | #> features : 4 139 | #> extent : 667850.3, 667941.1, 3538084, 3538143 (xmin, xmax, ymin, ymax) 140 | #> crs : NA 141 | #> variables : 2 142 | #> names : build_id, BLDG_HT 143 | #> min values : 365, 19.07 144 | #> max values : 831, 22.73
    plot(build, axes = TRUE) 145 |
    146 |
    147 | 152 |
    153 | 154 | 155 |
    156 | 159 | 160 |
    161 |

    Site built with pkgdown 1.6.1.

    162 |
    163 | 164 |
    165 |
    166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /docs/reference/beersheva_elev.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | DEM of Ramot neighborhood, Beer-Sheva — beersheva_elev • shadow 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 106 | 107 | 108 | 109 |
    110 | 111 |
    112 |
    113 | 118 | 119 |
    120 |

    Digital Elevation Model (DEM) of Ramot neighborhood, Beer-Sheva. Raster values represent elevation above sea level, in meters.

    121 |
    122 | 123 |
    beersheva_elev
    124 | 125 | 126 |

    Format

    127 | 128 |

    A RasterLayer representing a grid of 1974 raster cells, each cell is a 30*30 meters rectangle. Data source is the Shuttle Radar Topography Mission (SRTM) 1 Arc-Second Global dataset.

    129 |

    References

    130 | 131 |

    https://lta.cr.usgs.gov/SRTM1Arc

    132 | 133 |

    Examples

    134 |
    beersheva_elev 135 |
    #> class : RasterLayer 136 | #> dimensions : 47, 42, 1974 (nrow, ncol, ncell) 137 | #> resolution : 30, 30 (x, y) 138 | #> extent : 670636.6, 671896.6, 3461431, 3462841 (xmin, xmax, ymin, ymax) 139 | #> crs : +proj=utm +zone=36 +datum=WGS84 +units=m +no_defs 140 | #> source : memory 141 | #> names : elev_m 142 | #> values : 283.2574, 356.0146 (min, max) 143 | #>
    plot(beersheva_elev) 144 |
    145 |
    146 | 151 |
    152 | 153 | 154 |
    155 | 158 | 159 |
    160 |

    Site built with pkgdown 1.6.1.

    161 |
    162 | 163 |
    164 |
    165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /docs/reference/boston_build.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Polygonal layer of three buildings in Boston — boston_build • shadow 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 106 | 107 | 108 | 109 |
    110 | 111 |
    112 |
    113 | 118 | 119 |
    120 |

    A SpatialPolygonsDataFrame object representing the outlines of three buildings located in Central Boston. The attribute height_m contains building height, in meters.

    121 |
    122 | 123 |
    boston_build
    124 | 125 | 126 |

    Format

    127 | 128 |

    A SpatialPolygonsDataFrame with 10 features and 4 attributes:

    129 |
    objectid

    Building part ID

    130 |
    build_id

    Building ID

    131 |
    part_floor

    Number of floors for part

    132 |
    height_m

    Building height, in meters

    133 | 134 |
    135 | 136 | 137 |

    Examples

    138 |
    boston_build 139 |
    #> class : SpatialPolygonsDataFrame 140 | #> features : 10 141 | #> extent : 328949.6, 329130.9, 4690552, 4690772 (xmin, xmax, ymin, ymax) 142 | #> crs : +proj=utm +zone=19 +datum=WGS84 +units=m +no_defs 143 | #> variables : 4 144 | #> names : objectid, build_id, part_floor, height_m 145 | #> min values : 99167, 1, 1, 3.631387178664 146 | #> max values : 123842, 3, 8, 240.240312
    plot(boston_build, axes = TRUE) 147 |
    148 |
    149 | 154 |
    155 | 156 | 157 | 167 |
    168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | --------------------------------------------------------------------------------