1

I have a dataframe in R with three columns; latitude, longitude and cluster. I would like to export this data into a KML file for visualizing these clusters in Google Earth. Here's a snippet of my data:

    structure(list(longitude = c(-73.1576, 109.5244, 109.5235, 109.5237, 
109.5244, 19.9022, -73.1575, -73.1574, 109.5235, 19.9069, 109.5242, 
19.9023, -73.1579, -73.1578, -73.1594, 19.9051, 109.5242, 19.9009, 
-73.1575, -73.1578, -73.1592, -73.1577, 109.5235, 109.5246, 109.5243, 
-73.1578, 19.9052, -73.1569, 19.9022, 19.9061, -73.1595, 109.5238, 
-73.157, -73.1576, 19.9044, 109.5257, -73.1571, 109.5249, -73.1577, 
109.5245, -73.1573, 19.9021, 19.9022, 19.9029, -73.1575, -73.1573, 
-73.157, 109.5243, 19.907, 19.9031, -73.1574, -73.1569, 109.5245, 
-73.1593, 19.9057, 109.5242, 109.5236, 19.9054, 109.5235, 109.5241, 
19.9058, 19.9055, 109.5242, -73.1593, 109.5239, 19.9009, 109.5241, 
-73.1572, -73.1572, -73.1571, 109.5235, -73.1576, 109.5243, 19.9031, 
19.9021, 19.9021, 19.9008, 109.5245, 19.9022, 109.5239, 19.903, 
-73.1583, 109.5242, -73.1575, 19.9045, 19.9008, -73.1574, 19.9008, 
-73.1573, 109.5242, -73.1576, 19.9053, 109.5237, -73.1572, -73.157, 
19.9061, -73.158, 19.9007, 19.9028, 19.9058, 109.5245, 19.905, 
19.9031, -73.1574, -73.1584, 109.5236, 19.9052, 19.9055, 19.9022, 
19.9051, 109.5241, 109.5244, -73.1571, -73.157, -73.1583, 109.524, 
19.9061, -73.1572, 109.5241, 19.9058, 109.5243, 19.906, 109.5244, 
-73.1577, -73.1577, -73.1576, -73.1579, 109.5238, -73.1569, 109.5243, 
19.9045, 109.5238, 19.9054, -73.1578, 109.5237, 109.5236, -73.1571, 
19.9057, 109.5243, -73.1577, -73.1575, 109.5236, 19.9023, 109.5235, 
109.5237, 109.5236, 109.5234, -73.1573, 19.9044, -73.1578, 19.9022
), latitude = c(-37.0423, 21.4841, 21.482, 21.4827, 21.4839, 
39.6318, -37.0423, -37.0425, 21.4821, 39.6329, 21.4838, 39.6331, 
-37.0423, -37.0423, -37.0423, 39.6329, 21.4834, 39.6315, -37.0426, 
-37.0422, -37.0423, -37.0424, 21.4824, 21.4842, 21.4836, -37.0424, 
39.6329, -37.0424, 39.6331, 39.6277, -37.0428, 21.4827, -37.0424, 
-37.0425, 39.6326, 21.4826, -37.0425, 21.4836, -37.0425, 21.4835, 
-37.0423, 39.6304, 39.6332, 39.6325, -37.0422, -37.0426, -37.0423, 
21.4841, 39.6329, 39.6334, -37.0424, -37.0423, 21.4842, -37.0422, 
39.6325, 21.4837, 21.4825, 39.6329, 21.4822, 21.4837, 39.6326, 
39.6329, 21.4835, -37.0423, 21.4829, 39.6316, 21.4835, -37.0423, 
-37.0426, -37.0424, 21.4823, -37.0426, 21.4839, 39.6335, 39.6315, 
39.6316, 39.6317, 21.484, 39.6316, 21.4828, 39.6325, -37.0426, 
21.4833, -37.0425, 39.6325, 39.6315, -37.0426, 39.6316, -37.0425, 
21.4836, -37.0424, 39.6331, 21.4824, -37.0425, -37.0425, 39.6276, 
-37.0424, 39.6316, 39.6322, 39.6324, 21.4841, 39.6317, 39.6325, 
-37.0423, -37.0429, 21.4824, 39.633, 39.633, 39.6317, 39.633, 
21.4834, 21.484, -37.0423, -37.0426, -37.0425, 21.4833, 39.6324, 
-37.0424, 21.4836, 39.6325, 21.4835, 39.6277, 21.4842, -37.0422, 
-37.0423, -37.0422, -37.0424, 21.4828, -37.0425, 21.4837, 39.6326, 
21.4829, 39.633, -37.0421, 21.4828, 21.4823, -37.0426, 39.6326, 
21.4838, -37.0426, -37.0424, 21.4821, 39.6316, 21.4825, 21.4823, 
21.4822, 21.4823, -37.0424, 39.6325, -37.0425, 39.6315), cluster = c(2579, 
3050, 3050, 3050, 3050, 5222, 2579, 2579, 3050, 5222, 3050, 5222, 
2579, 2579, 2579, 5222, 3050, 5222, 2579, 2579, 2579, 2579, 3050, 
3050, 3050, 2579, 5222, 2579, 5222, 5222, 2579, 3050, 2579, 2579, 
5222, 3050, 2579, 3050, 2579, 3050, 2579, 5222, 5222, 5222, 2579, 
2579, 2579, 3050, 5222, 5222, 2579, 2579, 3050, 2579, 5222, 3050, 
3050, 5222, 3050, 3050, 5222, 5222, 3050, 2579, 3050, 5222, 3050, 
2579, 2579, 2579, 3050, 2579, 3050, 5222, 5222, 5222, 5222, 3050, 
5222, 3050, 5222, 2579, 3050, 2579, 5222, 5222, 2579, 5222, 2579, 
3050, 2579, 5222, 3050, 2579, 2579, 5222, 2579, 5222, 5222, 5222, 
3050, 5222, 5222, 2579, 2579, 3050, 5222, 5222, 5222, 5222, 3050, 
3050, 2579, 2579, 2579, 3050, 5222, 2579, 3050, 5222, 3050, 5222, 
3050, 2579, 2579, 2579, 2579, 3050, 2579, 3050, 5222, 3050, 5222, 
2579, 3050, 3050, 2579, 5222, 3050, 2579, 2579, 3050, 5222, 3050, 
3050, 3050, 3050, 2579, 5222, 2579, 5222)), row.names = c(NA, 
-151L), class = c("data.table", "data.frame"), .internal.selfref = <pointer: 0x10200e8e0>)

I created a list of spatialPolygondataframe's containing concave hulls as follows:

points_df <- split(points_df, points_df$cluster)
clusters <- names(points_df)
points_df <- lapply(points_df, function(x) { x$cluster <- NULL; x })
points_df <- lapply(points_df, st_as_sf, coords = c("longitude", "latitude"), crs = "+proj=longlat +datum=WGS84")
points_df <- lapply(points_df, concaveman)
points_df <- lapply(points_df, as_Spatial)

So I ended up with a list of SpatialPolygonsDataFrame's. Now I want to use the kmlPolygon function in maptools to generate a KML file for visualization in Google Earth as follows:

mycolors <- colorRampPalette(brewer.pal(8, "Set2"))(length(clusters))
kmlPolygon(obj=points_df, kmlfile="myfile.kml", col=mycolors)

This throws the following error:

Error in kmlPolygon(obj = points_df, kmlfile = "myfile.kml", col = mycolors) : 
  obj must be of class 'Polygons' or 'SpatialPolygonsDataFrame' [package 'sp']

Now if I use one element from the list (correctly):

kmlPolygon(obj=points_df[[1]]], kmlfile="myfile.kml", col=mycolors)

everything works and I am able to generate the kml file and visualize it in Google Earth. So my question is, how do I use the whole list of SpatialPolygonsDataFrame's - is there some sort of operation I need to do to combine them or ....?

8
  • How and why are you trying to make polygons from your cluster points? How do you know the points are in the right order to make a polygon? Your code wont run as-is because that data has 10 points and you can't make a polygon because the clusters have so few - in many cases one - points.
    – Spacedman
    Commented Jul 25, 2019 at 13:04
  • Thanks for the comment @Spacedman - I actually have ~5 millions points and ~5k clusters generated from DBSCAN, but I only shared a snippet of the data.
    – user76020
    Commented Jul 25, 2019 at 13:12
  • @Spacedman - I realized my error - I should have created a convex hull from the set of points and used that to create the polygon - I apologize for the inconvenience!
    – user76020
    Commented Jul 25, 2019 at 13:41
  • Does it fail the same way if you make a subset of your data with (say) 12 points in two clusters?
    – Spacedman
    Commented Jul 25, 2019 at 14:16
  • @Spacedman - yes, if I understand you correctly - I took a subset with the first 10 polygons and tried the kmlPolygon function and got the same warning message
    – user76020
    Commented Jul 25, 2019 at 15:37

1 Answer 1

0

I finally figured out the solution to this problem:

Using the same input from the question, and using the sp package and a workflow of Polygon >> Spatial Polygons >> Spatial Polygons dataframe (thanks R help; ?SpatialPolygons).

Prior to that I needed to find the concave hull for each cluster (using the concaveman package) and that required a matrix of coordinates which I created using the coordinates function in the sp package. (thanks to the question from @Spacedman - i realized this is needed)

points_df <- split(points_df, points_df$cluster) # Split points by cluster
clusters <- names(points_df)
points_df <- lapply(points_df, function(x) { x$cluster <- NULL; x })
points_df <- lapply(points_df, coordinates) # create coordinates
points_df <- lapply(points_df, concaveman) # calculate concave hull
points_df <- lapply(points_df, Polygon) # create Polygon
points_df <- lapply(seq_along(points_df), function(i) Polygons(list(points_df[[i]]), ID = clusters[i]  )) # Assign ID's
points_df <- SpatialPolygons(points_df, proj4string = CRS("+proj=longlat +datum=WGS84")) # Create spatial polygon
points_df <- SpatialPolygonsDataFrame(points_df, data.frame(i=1:5586,id = clusters, row.names = clusters)) # Create spatial polygons dataframe 
class(points_df)

mycolors <- colorRampPalette(col2rgb(brewer.pal(8, "Set2")))(length(clusters)) # set colors - but it doesn't work for some reason, am still investigating
kmlPolygons(obj=points_df, kmlfile="myfile.kml", col=mycolors) # write kml file

note that this uses the kmlPolygons function form maptools as opposed to the kmlPolygon - the distinction should be obvious from the name, though I think they could be merged into one function?

https://rdrr.io/cran/maptools/src/R/kmlPolygon.R

https://rdrr.io/cran/maptools/src/R/kmlPolygons.R

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.