-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathREADME.Rmd
190 lines (138 loc) · 6.69 KB
/
README.Rmd
1
2
3
4
5
6
7
8
9
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
---
output: github_document
editor_options:
chunk_output_type: console
---
<!-- README.md is generated from README.Rmd. Please edit that file -->
<!-- badges: start -->
[![R-CMD-check](https://github.com/hypertidy/polymer/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/hypertidy/polymer/actions/workflows/R-CMD-check.yaml)
<!-- badges: end -->
```{r setup, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
out.width = "100%"
)
```
# polymer
The goal of polymer is to provide flexible and intuitive overlay methods
(familiar to GIS workflows) but with any number of input layers.
The plot on the left shows 3 overlapping polygon layers, four squares on the
bottom, then a single triangle, and then a blue elongated rectangle. The next
panel shows the entire mesh with every input edge included, the purple region
has two overlappping polygons, the grey region has three. We can distinguish the
various layers by what parts of the plane they occupy, with finite elements that
collectively capture the input shapes.
```{r panels, echo=FALSE}
library(polymer)
par(mfrow = c(1, 2), mar = rep(0, 4))
plot(sf::st_geometry(A), col = viridis::viridis(nrow(A)), reset = FALSE)
plot(sf::st_geometry(B), col = "firebrick", add = TRUE)
plot(sf::st_geometry(C), col = "dodgerblue", add = TRUE)
pool <- polymer(A, B, C)
plot(pool, show_intersection = TRUE)
plot(layer_n(pool, n = 2), add = TRUE, col = "purple")
plot(layer_n(pool, n = 3), add = TRUE, col = "grey70")
```
This works via finite-element decomposition of all component edges in all inputs
as triangles. Triangle *instances* are classified (by point-in-polygon lookup)
by inclusion within paths within objects within layers.
There are two functions:
* `polymer()` build the triangle pool from 1 or more input layers
* `layer_n()` extract a simple features layer composed of n-overlaps from the inputs to `polymer()` (default is 2)
There are [print](https://mdsumner.github.io/polymer/reference/print.polymer.html) and [plot](https://mdsumner.github.io/polymer/reference/plot.polymer.html]) methods for the polymer pool.
We currently ***do not*** keep the input layers linked in the output from `layer_n()` but this will be a key feature in future versions. (You can set an argument to keep them if you are adventurous). We need some intermediate forms because sf itself cannot store multi-relations without duplicating geometries.
## WIP
* holes are identifiable but not yet explicitly classified
* extend sb_intersection to return the right parts
* write sensible return types and include nput attributes
## Installation
Install the development version from [GitHub](https://github.com/) with:
``` r
# install.packages("devtools")
devtools::install_github("mdsumner/polymer")
```
## Example
This example takes three built in data sets and merges them together as an indexed mesh.
```{r example}
library(polymer)
plot(sf::st_geometry(A), col = viridis::viridis(nrow(A)))
plot(sf::st_geometry(B), col = "firebrick", add = TRUE)
plot(sf::st_geometry(C), col = "dodgerblue", add = TRUE)
## summarize the contents
(pool <- polymer(A, B, C))
## show the components pieces
plot(pool, asp = 1)
```
The next stage pulls out the intersection layer, currently we only have a function
to plot the identified triangles - but work to come will identify them individually and copy attributes from the input layers appropriately.
```{r}
plot(pool, col = "firebrick", show_intersection = TRUE)
## it works with pairs or with multiple layers
plot(polymer(A, B), col = "firebrick", show_intersection = TRUE)
plot(polymer(C, B), col = "firebrick", show_intersection = TRUE)
set.seed(sum(match(unlist(strsplit("polymer", "")), letters)))
## number of layers is arbitrary
plot(polymer(C, B, A, sf::st_jitter(A, 0.1)), col = "firebrick", show_intersection = TRUE)
```
A function `layer_n` will pull out any >=n overlaps.
```{r nintersections}
plot(A["layer"], col = viridis::viridis(nrow(A)), reset = FALSE)
plot(B, add = TRUE, col = "hotpink")
plot(C, add = TRUE, col = "firebrick")
```
```{r}
plot(A["layer"], col = viridis::viridis(nrow(A)), reset = FALSE)
plot(B, add = TRUE, col = "hotpink")
plot(C, add = TRUE, col = "firebrick")
sb <- polymer(A, B, C)
plot(layer_n(sb), add = TRUE, col = "grey")
plot(layer_n(sb, n = 3), add = TRUE, col = "dodgerblue")
x <- layer_n(sb, n = 3)
## see how we know the identity of each input layer
tibble::as_tibble(x) %>% dplyr::select(-geometry) %>% tidyr::unnest()
```
```{r so-example}
plot(soil, col = sf::sf.colors(n = nrow(soil)), border = NA, reset = FALSE)
plot(field, add = TRUE, col = NA)
soil_field <- polymer(soil, field)
plot(layer_n(soil_field), add = TRUE, border = rgb(0.5, 0.5, 0.5, 0.2))
```
From `vignette("over", package = "sp")`.
```{r over}
library(sp)
library(sf)
x = c(0.5, 0.5, 1.0, 1.5)
y = c(1.5, 0.5, 0.5, 0.5)
xy = cbind(x,y)
dimnames(xy)[[1]] = c("a", "b", "c", "d")
pts = SpatialPoints(xy)
xpol = c(0,1,1,0,0)
ypol = c(0,0,1,1,0)
pol = SpatialPolygons(list(
Polygons(list(Polygon(cbind(xpol-1.05,ypol))), ID="x1"),
Polygons(list(Polygon(cbind(xpol,ypol))), ID="x2"),
Polygons(list(Polygon(cbind(xpol,ypol - 1.0))), ID="x3"),
Polygons(list(Polygon(cbind(xpol + 1.0, ypol))), ID="x4"),
Polygons(list(Polygon(cbind(xpol+.4, ypol+.1))), ID="x5")
))
pol <- st_as_sf(SpatialPolygonsDataFrame(disaggregate(pol), data.frame(a = 1:5)))
(polb <- polymer(pol[1, ], pol[2, ], pol[3, ], pol[4, ], pol[5, ]))
plot(polb, reset = FALSE)
plot(layer_n(polb), add = TRUE, col = rgb(0, 0, 0, 0.3), border = "firebrick", lwd = 2)
```
## Background details
The resulting mesh and inputs and indexes can be used to derive complex
relationships between layers. polymer is modelled on the concept of **data
fusion** from a now defunct commercial package called Eonfusion. It relies on
the RTriangle package which is licensed CC BY-NC-SA 4.0, but could be modified
to use the less restrictive `decido` package. Specialist forms of this might
choose other engines - the crux is constrained triangulation, and for planar shapes high-quality triangles aren't required so long as all inputs edges are preserved.
This is analogous to what GIS packages variously call "overlay", "topology
overlay", "intersection" and so on. The difference is we want a single mesh that
has information about all of its inputs in a lossless form. We can derive
general information from the mesh and the links to sources without simplifying
everything to a single result that has no connection to the sources.
---
Please note that the polymer project is released with a [Contributor Code of Conduct](https://contributor-covenant.org/version/1/0/0/CODE_OF_CONDUCT.html). By contributing to this project, you agree to abide by its terms.