How can you smooth the polygons of a map produced with ggplot and sf?
I have used the sf package to extract the polygons from a shapefile
geomunicipios <- st_read("ruta/archivo.shp")
Reading layer `archivo' from data source
`ruta\archivo.shp'
using driver `ESRI Shapefile'
Simple feature collection with 45 features and 10 fields
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: -2.344411 ymin: 37.37375 xmax: -0.647983 ymax: 38.75509
Geodetic CRS: WGS 84
And ggplot2 to plot the map:
rmurcia <- ggplot(data = geomunicipios) +
geom_sf(aes(fill=columna),color="#FFFFFF",size=1)
To perform the smoothing of the polygons I have analyzed three alternatives:
i. package "smoothr":
geosmunicipios <- smooth(geomunicipios, method = "ksmooth", smoothness = 12)
ii. package "rmapshaper": geosmunicipios <- ms_simplify(geomunicipios, keep = 0.02500, weighting = 12)
iii. package "sf": geosmunicipios <- st_simplify(geomunicipios, dTolerance = 50, preserveTopology = TRUE)
You have to try different values of the parameters to adjust to the needs and obtain the desired result.
To reproduce the case, the download can be done from: centrodedescargas.cnig.es/CentroDescargas/index.jsp
And follow the links:
Información geográfica de referencia - Límites municipales, provinciales y autonómicos - Descargar: lineas_limite.zip.
And the path in the uncompressed folder:
SIGLIM_Publico_INSPIRE - SHP_ETRS89 - recintos_municipales_inspire_peninbal_etrs89 - recintos_municipales_inspire_peninbal_etrs89.shp
Finally, for this case I have chosen to use rmapshaper, it produces a satisfactory result with a reduced size of the .pdf file, where I include the graphic.
Related
I have a shape file that I can read like this in R:
library(rgdal)
shape <- readOGR(dsn = "~/path", layer = "a")
I am interested in the whole region that cover all polygons (black curve here). How to dissolve all polygons even those separated into one polygon like this?
I am open to solutions from R or Qgis
Using R & the sf package you can make a convex hull of the unioned (if necessary) shapefile. Since you haven't included data, I've used the nc data included with the sf package to illustrate the method.
library(dplyr)
library(sf)
library(ggplot2)
# setting up sample data,
# you'll need to use st_read() to read your shapefile, not readOGR()
nc <- st_read(system.file("shape/nc.shp", package="sf"))
#> Reading layer `nc' from data source
#> `.../sf/shape/nc.shp'
#> using driver `ESRI Shapefile'
#> Simple feature collection with 100 features and 14 fields
#> Geometry type: MULTIPOLYGON
#> Dimension: XY
#> Bounding box: xmin: -84.32385 ymin: 33.88199 xmax: -75.45698 ymax: 36.58965
#> Geodetic CRS: NAD27
nc <- nc[c(1:30, 85:81),] #Use some non-contiguous counties
# make a convex hull of the unioned geometries
nc_hull <- st_convex_hull(st_union(nc))
ggplot() +
geom_sf(data = nc, fill = NA, color = 'red') +
geom_sf(data = nc_hull, fill = NA, color = 'black')
Created on 2022-03-18 by the reprex package (v2.0.1)
I am cleaning my dataset and I don't know how to clean GPS data.
when I use the table function I find that they are entered in different shapes.
"547140",
"35.6997",
"251825.7959",
"251470.43",
"54/4077070001",
and "54/305495"
I don't know how to clean this variable with this great difference.
I would be thankful if help me or suggest me a website for training.
Your main issue is standardizing the GPS by projecting GPS to a coordinate system of choice. Say we have the GPS of amsterdam in two different coordinate systems, one in amersfoort/rd new (espg 28992) and one in wsg1984 (espg 4326):
x y location espg
1: 1.207330e+05 486632.35593 amsterdam 28992
2: 4.884088e+00 52.36651 amsterdam 4326
structure(list(x = c(120733.012428048, 4.88408811380055), y = c(486632.355933105,
52.3665054922233), location = c("amsterdam", "amsterdam"), espg = c(28992,
4326)), row.names = c(NA, -2L), class = "data.frame")
What we want to do is reproject our coordinates to one geographic coordinate system of choice. In this case I used WSG1984 (espg 4326).
library(sf)
#here I tell R which columns contain the coordinates
coordinates(dt) <- ~x+y
#I now convert the table to a spatial object
dt <- st_as_sf(dt)
#here I split by the different ESPG's present
dt <- split(dt, dt$espg)
#here I loop through every individual espg present in the dataset
for(i in 1:length(dt)){
#here I say in which coordinate system (espg) the GPS data is in
st_crs(dt[[i]]) <- unique(dt[[i]]$espg)
#here I transform the coordinates to another projection (in this case WSG1984, espg 4326)
dt[[i]] <- dt[[i]] %>% st_transform(4326)
}
#here I bind the items of the list together
dt <- do.call(rbind, dt)
head(dt)
Simple feature collection with 2 features and 2 fields
Geometry type: POINT
Dimension: XY
Bounding box: xmin: 4.884088 ymin: 52.36651 xmax: 4.884088 ymax: 52.36651
Geodetic CRS: WGS 84
location espg geometry
4326 amsterdam 4326 POINT (4.884088 52.36651)
28992 amsterdam 28992 POINT (4.884088 52.36651)
In the geometry column you now see that the coordinates are equal to one another.
Bottom line is that you need to know the geographic coordinate system the GPS data is in. Then you can convert your data from a table to a spatial object and transform the GPS data to a projection of choice.
In addition, it is always a good idea to check if your assumption on the original ESPG is good by for example plotting the data.
library(ggplot2)
library(ggspatial)
ggplot(dt) + annotation_mape_tile() + geom_sf(size = 4) + theme(text = element_text(size = 15) + facet_wrap(~espg)
In the figurebelow we see that the projection went well for both espg's.
I am new to geospatial data & trying to plot using .shp file but getting an error.
The geometry type in this .shp file is LINESTRING which seems to be different from MULTIPOLYGON which I have plotted before using sf
shape file source: https://github.com/johnsnow09/covid19-df_stack-code/blob/main/in_country_boundaries.shp
original source of shapefile: https://github.com/wri/wri-bounds/blob/master/dist/in_countries.zip
Expecting result:
code attempt:
library(tidyverse)
library(sf)
ind_global <- sf::read_sf("path/in_country_boundaries.shp")
ind_global
output
Simple feature collection with 412 features and 9 fields
Geometry type: LINESTRING
Dimension: XY
Bounding box: xmin: -141.0056 ymin: -54.88624 xmax: 140.9776 ymax: 70.07531
Geodetic CRS: WGS 84
ind_global %>%
st_as_sf() %>%
ggplot() +
geom_sf()
Error in st_cast.POINT(X[[i]], ...) : cannot create MULTILINESTRING
from POINT
Do I need to handle LINESTRING geometry .shp file in some other way?
The code is running fine after removing st_as_sf() %>%. I have downloaded the shapefile from https://github.com/wri/wri-bounds/blob/master/dist/in_countries.zip and it is MULTIPOLYGON only.
library(tidyverse)
library(sf)
ind_global <- sf::read_sf("...\\in_countries.shp")
ind_global
ind_global %>%
ggplot() +
geom_sf()
I Have a shapefile of polygons and another one of points that are distributed over the polygons. I would like to create a kernel density estimate for each polygon based on the points it contains. unfortunately I was only able to create squared KDEs with the kde2d function from the MASS package. I would like the KDEs to be shaped as the polygons.
Any suggestions?
kde1 <- kde2d(poly$X, poly$Y, n=100,)
enter image description here
You can use the spatstat package for this. Here is an example of reading
in a shapefile from sf, generating random points and run kernel density
estimation of the intensity of points (points per unit area):
library(sf)
#> Linking to GEOS 3.8.0, GDAL 3.0.4, PROJ 6.3.1
nc <- st_read(system.file("shape/nc.shp", package="sf"))
#> Reading layer `nc' from data source `/usr/lib/R/site-library/sf/shape/nc.shp' using driver `ESRI Shapefile'
#> Simple feature collection with 100 features and 14 fields
#> geometry type: MULTIPOLYGON
#> dimension: XY
#> bbox: xmin: -84.32385 ymin: 33.88199 xmax: -75.45698 ymax: 36.58965
#> geographic CRS: NAD27
nc_flat <- st_transform(nc, crs = 26917)
W <- as.owin(nc_flat$geometry[1]) # First county of North Carolina data set in spatstat format
library(spatstat)
X <- runifpoint(100, win = W)
plot(X, "Random points")
D <- density(X)
plot(D, main = "KDE")
OK! I managed to use my own points by using the 'ppp' function from the spatstat package.
C <- as.owin(polygon$geometry[n])
p<- ppp(points$X,points$Y, window = C)
D <- density(p)
[enter image description here][1]
[1]: https://i.stack.imgur.com/YZN0V.png
I'm trying to reduce the size of sf object by applying st_simplify. CRS is 4267 and try to play around with the right level of dTolerance. I understand that the unit of dTolerance has to be that of the CRS, so I started with 0.1, but I constantly getting this error message.
test <- st_read("comm_sf.shp") %>%
+ st_simplify(preserveTopology = T,
+ dTolerance = 0.1)
Simple feature collection with 11321 features and 21 fields
geometry type: MULTIPOLYGON
dimension: XY
bbox: xmin: -124.4375 ymin: 24.5441 xmax: -66.94983 ymax: 49.00249
epsg (SRID): 4326
proj4string: +proj=longlat +datum=WGS84 +no_defs
Warning message:
In st_simplify.sfc(st_geometry(x), preserveTopology, dTolerance) :
st_simplify does not correctly simplify longitude/latitude data, dTolerance needs to be in decimal degrees
I play around with both setting dTolerance = 1000 (in case it's in meters) and dTolerance = 0.1 (in case it's in long/lat), but I get the same error message. This happens with CRS = 4267 as well. How can I fix this?
Well its a warning rather than an error. But in general you should do Douglas-Peucker on a projected coordinate system - because it uses a distance as a buffer, whereas the actual size of a unit of longitude varies with latitude. Note that the unit used by st_simplify tolerance will always be in the same as the map units.
Here's a reproducible example:
library(sf)
library(maptools)
states = st_as_sf(maps::map("state", plot = FALSE, fill = TRUE))
states_simple = st_simplify(states)
##Warning message:
## In st_simplify.sfc(st_geometry(x), preserveTopology, dTolerance) :
## st_simplify does not correctly simplify longitude/latitude data, dTolerance needs to be in decimal degrees
But if we transform to a projected coordinate system first, then no warning:
states = st_transform(states, 54032) #azimuthal equidistant
states_simple = st_simplify(states)
You can always go back to WGS84 lat-long after the simplification
states = st_transform(states, 4326)