I am relatively new to R so i apologize if I have trouble expressing what I'm attempting to do. I have a 'spatial' panel dataset in long form and a shapefile. The long form table is a data.frame and it includes a column of dates (that have been converted to dates using 'as.date') and an ID column that is the same as that in the shapefile used to identify the different polygons (thus my long form dataset has no long lat values just an ID field that corresponds to the polygon features in the shapefile). I want to construct a spatiotemporal object of class ST out of these two objects (the shapefile and the long form dataset). To do this I have tried using stcontruct() and STFDF() but with absolutely not luck. stcontruct() gives me this error:
stConstruct(x, x$ID, x$date, SpatialObj = pol, TimeObj = NULL, interval=FALSE)
Error in stConstruct(x, x$ID, x$date, SpatialObj = pol, TimeObj = NULL, :
unknown parameter combination
and STFDF() gives me this error:
STFDF(shapefile, x$date, x)
Error: nrow(object#data) == length(object#sp) * nrow(object#time) is not TRUE
I've been stuck on this for days reading everything I can about the spacetime package in forums, etc. but to no avail. Any help is greatly appreciated.
thanks!
About STFDF error
From:
http://r-sig-geo.2731867.n2.nabble.com/Error-with-STFDF-td7584461.html
"If you don't have every time value at every spatial point, then you can't
have an STFDF object, as by definition STFDF is a full space by time grid.
The equation in the error is part of the definition/requirement for an
STFDF object.
SDIDF objects don't have that requirement of all times at all locations..."
Related
Completely new to R, forgive me -
I'm trying to use R to create some historic OSM data thats stored as a sf (simple feature) within the R script. I'd like to export this data once called as a shapefile (or GEOJSON) readable by QGIS. The reason I am doing this is that it seems to me that extracting specific historic datasets via Rs osmdata package is the most efficient way of creating data of specific segments of OSM historic data for a given period in its editing history (Let me know if anyone have a faster way of doing this for country-sized batches of data for different years.)
My current code, using a sample dataset, looks like this:
library(osmdata)
library(sf)
q1 <- opq('Sevilla') %>%
add_osm_feature(key = 'highway', value = 'cycleway')
cway_sev <- osmdata_sp(q1)
sp::plot(cway_sev$osm_lines)
I'm getting two different types of errors:
When I add a specific datetime (like this: q1 <- opq('Sevilla',datetime = "2015-01-01T12:00:00Z") %>%), I'm getting this error:
Error in check_for_error(doc) : General overpass server error; returned:
The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. runtime error: Query timed out in "query" at line 4 after 45 seconds.
Additionally, and I'm guessing more improtantly, when I add the function to convert from an SF to a SHP
(st_write(cway_sev, "sev_t_1.shp"))
I get this error:
Error in UseMethod("st_write") :
no applicable method for 'st_write' applied to an object of class "c('list', 'osmdata', 'osmdata_sp')"
Any advice? Again, complete R newbie here.
I can not help you with the timeout on a historic datetime; there may be a server side issue for all that I know (I get the same error, and your construction of the argument seems to follow documentation).
Regarding the other issues:
When working in the world of {sf} I suggest to download your data via osmdata_sf() call; it will be good for your sanity if you avoid mixing sf and sp worlds unless absolutely necessary.
The returned object will contain not only lines, but also points, polygons and (in your case empty) multi-type objects.
When working with cycle paths just select the osm_lines object to a new variable; it will contain Sevilla bike paths with geometry of type linestring.
After checking it visually you can now save it as a ESRI Shapefile; note that this is an ancient file format, based on Ashton Tate dBase IV (a DOS program for Pete's sake :) and as such allows data column names of only limited number of characters, hence the warning.
library(osmdata)
library(dplyr)
library(sf)
sevilla <- opq('Sevilla') %>%
add_osm_feature(key = 'highway', value = 'cycleway') %>%
osmdata_sf()
names(sevilla) # note the points, polygons, multilines and multipolygons
# [1] "bbox" "overpass_call" "meta" "osm_points" "osm_lines"
# [6] "osm_polygons" "osm_multilines" "osm_multipolygons"
# just a single geometry type
sevilla_lines <- sevilla$osm_lines
# visual check of lines geometry only / does this look right?
plot(st_geometry(sevilla_lines))
# this will work, with the lines only
st_write(sevilla_lines, "sevilla.shp")
# Writing layer `sevilla' to data source `sevilla.shp' using driver `ESRI Shapefile'
# Writing 555 features with 34 fields and geometry type Line String.
# Warning message:
# In abbreviate_shapefile_names(obj) :
# Field names abbreviated for ESRI Shapefile driver
I am trying to calculate the centroids of a set of polygons.
My dataset, geodata, contains five columns including one geometry column of class sfc_GEOMETRY, with 45759 rows.
When I run sf::st_centroid(geodata), I get the following message
Error in CPL_geos_op("centroid", x, numeric(0), integer(0), numeric(0), : Evaluation error: IllegalArgumentException: Points of LinearRing do not form a closed linestring.
In addition: Warning messages:
1: In st_centroid.sf(geodata) :
st_centroid assumes attributes are constant over geometries of x
2: In st_centroid.sfc(st_geometry(x), of_largest_polygon = of_largest_polygon) :
st_centroid does not give correct centroids for longitude/latitude data
Should I run a loop to detect which geometry is not closed?
Is this a problem with the class of my geometry? Should it be sfc_MULTIPOLYGON?
Possible solution:
I was encountering this problem when reading in a list of files through a loop. The loop would read in the files and then rbind them together into geodata, and then calculate the centroid:
for(i in 1:length(list)){
file <- st_read(list[i])
geodata <- rbind(geodata, file) #geodata is here a void sf object
}
geocent <- st_centroid(geodata)
When I calculated the centroids within the loop (for each file in the list), the error disappeared.
for(i in 1:length(list)){
file <- st_read(list[i])
file <- st_centroid(file)
geocent <- rbind(geodata, file) #geodata is here a void sf object
}
Hence, I think the problem lay in the binding operation.
Perhaps I had not defined my void sf object in the right manner.
Perhaps rbind was not the appropriate function, or I should have specified its parameters.
There's no need to run a loop to find which geometry is bad. The st_is_valid() function should tell you which row(s) have the problem.
It looks like one of your geometries might be made up of an incorrect number of points.
More info about finding and fixing the problem at r-spatial: https://www.r-spatial.org/r/2017/03/19/invalid.html
I am hoping that you can support me with the use of the getSunlightTimes function. I have a pixel level data frame ("latlon2") with latitude ("lat"), longitude ("lon"), and one date ("date") in format YYYY-MM-DD. The data covers the continental US, and I also have a state code variable in the data frame.
To obtain the date variable as a date class variable, I executed:
latlon2$date=as.Date(latlon2$d2003s)
I am trying to use the getSunlightTimes to identify the time of sunrise and sunset for each pixel on the designated date. However, I am having a hard time getting the function to work. There is not a lot of information on this command beyond R's help guides, so I am hoping some of you have worked with it and can offer your suggestions based on my approach so far.
First I tried using the getSunlightTimes function designating each latitude/longitude/date column in my data frame
sunrise2003CET=getSunlightTimes(date="latlon2$date", lat="latlon2$lat", lon="latlon2$lon", tz="CET", keep = c("sunrise", "sunset"))
R returns the error:
Error in getSunlightTimes(date = "latlon2$date2", lat = "latlon2$lat",
: date must to be a Date object (class Date)
What's frustrating about this is that when I look at class(latlon2$date) R verifies that the column is a "Date" class!
Next, I tried designating the data frame only:
sunrise2003CET=getSunlightTimes(data="latlon2", tz="CET", keep = c("sunrise", "sunset"))
R returns the error:
Error in .buildData(date = date, lat = lat, lon = lon, data = data) :
all(c("date", "lat", "lon") %in% colnames(data)) is not TRUE
This seems odd because I named the columns in the dataframe "date", "lat", "lon", but perhaps the error is due to the fact that there are other variables in the data frame (such as state code).
I am trying to perform this task for several dates across 15 years (and four time zones), so any suggestions on how to get this running, and also running efficiently, are much appreciated!
Thank you so much!
Colette
The problem is with the quotes. When you write
sunrise2003CET=getSunlightTimes(date="latlon2$date",
lat="latlon2$lat",
lon="latlon2$lon",
tz="CET",
keep = c("sunrise", "sunset"))
you shouldn't put the expressions for the date, lat and lon arguments in quotes, because then R will see them as strings. (You could try class("latlon2$date") to see this.) Just write it as
sunrise2003CET=getSunlightTimes(date=latlon2$date,
lat=latlon2$lat,
lon=latlon2$lon,
tz="CET",
keep = c("sunrise", "sunset"))
I realise this has been asked about 100 times prior, but none of the answers I've read so far on SO seem to fit my problem.
I have data. I have the lat and lon values. I've read around about something called sp and made a bunch of shape objects in a dataframe. I have matched this dataframe with the variable I am interested in mapping.
I cannot for the life of me figure out how the hell to get ggplot2 to draw polygons. Sometimes it wants explicit x,y values (which are a PART of the shape anyway, so seems redundant), or some other shape files externally which I don't actually have. Short of colouring it in with highlighters, I'm at a loss.
if I take an individual sps object (built with the following function after importing, cleaning, and wrangling a shitload of data)
createShape = function(sub){
#This funciton takes the list of lat/lng values and returns a SHAPE which should be plottable on ggmap/ggplot
tempData = as.data.frame(do.call(rbind, as.list(VICshapes[which(VICshapes$Suburb==sub),] %>% select(coords))[[1]][[1]]))
names(tempData) = c('lat', 'lng')
p = Polygon(tempData)
ps = Polygons(list(p),1)
sps = SpatialPolygons(list(ps))
return(sps)
}
These shapes are then stored in the same dataframe as my data - which only this afternoon for some reason, I can't even look at, as trying to look at it yields the following error.
head(plotdata)
Error in prettyNum(.Internal(format(x, trim, digits, nsmall, width, 3L, : first argument must be atomic
I realise I'm really annoyed at this now, but I've about 70% of a grade riding on this, and my university has nobody capable of assisting.
I have pasted the first few rows of data here - https://pastebin.com/vFqy5m5U - apparently you can't print data with an s4 object - the shape file that I"m trying to plot.
Anyway. I'm trying to plot each of those shapes onto a map. Polygons want an x,y value. I don't have ANY OTHER SHAPE FILES. I created them based on a giant list of lat and long values, and the code chunk above. I'm genuinely at a loss here and don't know what question to even ask. I have the variable of interest based on locality, and the shape for each locality. What am I missing?
edit: I've pasted the summary data (BEFORE making them into shapes) here. It's a massive list of lat/lng values for EACH tile/area, so it's pretty big...
Answered on gis.stackexchange.com (link not provided).
Using leaflet, I'm trying to plot some lines and set their color based on a 'speed' variable. My data start at an encoded polyline level (i.e. a series of lat/long points, encoded as an alphanumeric string) with a single speed value for each EPL.
I'm able to decode the polylines to get lat/long series of (thanks to Max, here) and I'm able to create segments from those series of points and format them as a SpatialLines object (thanks to Kyle Walker, here).
My problem: I can plot the lines properly using leaflet, but I can't join the SpatialLines object to the base data to create a SpatialLinesDataFrame, and so I can't code the line color based on the speed var. I suspect the issue is that the IDs I'm assigning SL segments aren't matching to those present in the base df.
The objects I've tried to join, with SpatialLinesDataFrame():
"sl_object", a SpatialLines object with ~140 observations, one for each segment; I'm using Kyle's code, linked above, with one key change - instead of creating an arbitrary iterative ID value for each segment, I'm pulling the associated ID from my base data. (Or at least I'm trying to.) So, I've replaced:
id <- paste0("line", as.character(p))
with
lguy <- data.frame(paths[[p]][1])
id <- unique(lguy[,1])
"speed_object", a df with ~140 observations of a single speed var and row.names set to the same id var that I thought I created in the SL object above. (The number of observations will never exceed but may be smaller than the number of segments in the SL object.)
My joining code:
splndf <- SpatialLinesDataFrame(sl = sl_object, data = speed_object)
And the result:
row.names of data and Lines IDs do not match
Thanks, all. I'm posting this in part because I've seen some similar questions - including some referring specifically to changing the ID output of Kyle's great tool - and haven't been able to find a good answer.
EDIT: Including data samples.
From sl_obj, a single segment:
print(sl_obj)
Slot "ID":
[1] "4763655"
[[151]]
An object of class "Lines"
Slot "Lines":
[[1]]
An object of class "Line"
Slot "coords":
lon lat
1955 -74.05228 40.60397
1956 -74.05021 40.60465
1957 -74.04182 40.60737
1958 -74.03997 40.60795
1959 -74.03919 40.60821
And the corresponding record from speed_obj:
row.names speed
... ...
4763657 44.74
4763655 34.8 # this one matches the ID above
4616250 57.79
... ...
To get rid of this error message, either make the row.names of data and Lines IDs match by preparing sl_object and/or speed_object, or, in case you are certain that they should be matched in the order they appear, use
splndf <- SpatialLinesDataFrame(sl = sl_object, data = speed_object, match.ID = FALSE)
This is documented in ?SpatialLinesDataFrame.
All right, I figured it out. The error wasn't liking the fact that my speed_obj wasn't the same length as my sl_obj, as mentioned here. ("data =
object of class data.frame; the number of rows in data should equal the number of Lines elements in sl)
Resolution: used a quick loop to pull out all of the unique lines IDs, then performed a left join against that list of uniques to create an exhaustive speed_obj (with NAs, which seem to be OK).
ids <- data.frame()
for (i in (1:length(sl_obj))) {
id <- data.frame(sl_obj#lines[[i]]#ID)
ids <- rbind(ids, id)
}
colnames(ids)[1] <- "linkId"
speed_full <- join(ids, speed_obj)
speed_full_short <- data.frame(speed_obj[,c(-1)])
row.names(speed_full_short) <- speed_full$linkId
splndf <- SpatialLinesDataFrame(sl_obj, data = speed_full_short, match.ID = T)
Works fine now!
I may have deciphered the issue.
When I am pulling in my spatial lines data and I check the class it reads as
"Spatial Lines Data Frame" even though I know it's a simple linear shapefile, I'm using readOGR to bring the data in and I believe this is where the conversion is occurring. With that in mind the speed assignment is relatively easy.
sl_object$speed <- speed_object[ match( sl_object$ID , row.names( speed_object ) ) , "speed" ]
This should do the trick, as I'm willing to bet your class(sl_object) is "Spatial Lines Data Frame".
EDIT: I had received the same error as OP, driving me to check class()
I am under the impression that the error that was populated for you is because you were trying to coerce a data frame into a data frame and R wasn't a fan of that.