1 km circula alrededor de puntos largos en muchos lugares del mundo

11

Tengo cientos de puntos lat-long repartidos por todo el mundo, y debo crear polígonos circulares alrededor de cada uno de ellos, con un radio de exactamente 1000 metros. Entiendo que los puntos se deben proyectar primero desde grados (lat largo) a algo con unidades de medidor, pero ¿cómo se puede hacer esto sin buscar y definir manualmente las zonas UTM para cada punto?

Aquí hay una idea para el primer punto en Finlandia.

library(sp)
library(rgdal)
library(rgeos)
the.points.latlong <- data.frame(
  Country=c("Finland", "Canada", "Tanzania", "Bolivia", "France"),
  lat=c(63.293001, 54.239631, -2.855123, -13.795272, 48.603949),
  long=c(27.472918, -90.476303, 34.679950, -65.691146, 4.533465))
the.points.sp <- SpatialPointsDataFrame(the.points.latlong[, c("long", "lat")], data.frame(ID=seq(1:nrow(the.points.latlong))), proj4string=CRS("+proj=longlat +ellps=WGS84 +datum=WGS84"))

the.points.projected <- spTransform(the.points.sp[1, ], CRS( "+init=epsg:32635" ))  # Only first point (Finland)
the.circles.projected <- gBuffer(the.points.projected, width=1000, byid=TRUE)
plot(the.circles.projected)
points(the.points.projected)

the.circles.sp <- spTransform(the.circles.projected, CRS("+proj=longlat +ellps=WGS84 +datum=WGS84"))

Pero con el segundo punto (Canadá) no funciona (porque la zona UTM es incorrecta).

the.points.projected <- spTransform(the.points.sp[2, ], CRS( "+init=epsg:32635" ))

¿Cómo se puede hacer esto sin obtener y especificar manualmente la zona UTM punto por punto? No tengo más información por punto que lat largo.

Actualizar:

Usando y combinando las excelentes respuestas de AndreJ y Mike T, aquí está el código para ambas versiones y tramas. Son diferentes en el cuarto decimal más o menos, ¡pero ambas son muy buenas respuestas!

gnomic.buffer <- function(p, r) {
  stopifnot(length(p) == 1)
  gnom <- sprintf("+proj=gnom +lat_0=%s +lon_0=%s +x_0=0 +y_0=0",
                  p@coords[[2]], p@coords[[1]])
  projected <- spTransform(p, CRS(gnom))
  buffered <- gBuffer(projected, width=r, byid=TRUE)
  spTransform(buffered, p@proj4string)
}

custom.buffer <- function(p, r) {
  stopifnot(length(p) == 1)
  cust <- sprintf("+proj=tmerc +lat_0=%s +lon_0=%s +k=1 +x_0=0 +y_0=0 +ellps=WGS84 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs", 
                  p@coords[[2]], p@coords[[1]])
  projected <- spTransform(p, CRS(cust))
  buffered <- gBuffer(projected, width=r, byid=TRUE)
  spTransform(buffered, p@proj4string)
}

test.1 <- gnomic.buffer(the.points.sp[2,], 1000)
test.2 <- custom.buffer(the.points.sp[2,], 1000)

library(ggplot2)
test.1.f <- fortify(test.1)
test.2.f <- fortify(test.2)
test.1.f$transf <- "gnomic"
test.2.f$transf <- "custom"
test.3.f <- rbind(test.1.f, test.2.f)

p <- ggplot(test.3.f, aes(x=long, y=lat, group=transf))
p <- p + geom_path()
p <- p + facet_wrap(~transf)
p

(No estoy seguro de cómo incluir la trama en la actualización).

Chris
fuente
1
Una posible solución a la parte de búsqueda manual: ¿Qué sucede si obtiene una cuadrícula de zona UTM e intersecta eso con sus puntos, de modo que agregue la zona apropiada como un atributo? El atributo podría ser el nombre de la zona o el código EPSG, pero algo que podría introducirse como una variable para seleccionar automáticamente el CRS correcto para cada punto.
Chris W
1
Tengo un problema con "exactamente 1000m" y la frase "círculo-polígonos". Sus polígonos circulares necesitan segmentos infinitos para tener exactamente 1000 m, y la conversión a UTM (o cualquier otro sistema plano) introducirá aún más errores. Tenga cuidado con el uso de "exacto".
Spacedman
Sí, no debería haberlo expresado de manera diferente. Quise decir que 1100m o 900m estarían demasiado apagados, y que unos 20 segmentos en el círculo están bien.
Chris

Respuestas:

9

Similar a @AndreJ, pero usa una proyección gnómica dinámica , me refiero a una proyección equidistante azimutal dinámica para una mayor precisión. Una proyección AEQ centrada en cada punto proyectará distancias iguales en todas las direcciones, como un círculo amortiguado. (Una proyección de Mercator tendrá algunas distorsiones en las direcciones norte y este, ya que se envuelve alrededor del lado de un cilindro).

Entonces, para su primer punto alrededor de Finlandia, la cadena PROJ.4 se verá así :

+proj=aeqd +lat_0=63.293001 +lon_0=27.472918 +x_0=0 +y_0=0

Entonces puede hacer una función R para hacer esta proyección dinámica:

aeqd.buffer <- function(p, r)
{
    stopifnot(length(p) == 1)
    aeqd <- sprintf("+proj=aeqd +lat_0=%s +lon_0=%s +x_0=0 +y_0=0",
                    p@coords[[2]], p@coords[[1]])
    projected <- spTransform(p, CRS(aeqd))
    buffered <- gBuffer(projected, width=r, byid=TRUE)
    spTransform(buffered, p@proj4string)
}

Luego haga algo como esto para Canadá (elemento 2):

aeqd.buffer(the.points.sp[2,], 1000)
Mike T
fuente
1
Desde la página de Wikipedia: "No se produce distorsión en el punto tangente, pero la distorsión aumenta rápidamente lejos de él". ¿Has hecho un cálculo de desplazamiento de muestra? Quizás en.wikipedia.org/wiki/Azimuthal_equidistant_projection sea ​​más adecuado.
AndreJ
2
Cualquier proyección que tenga la escala correcta en el origen del círculo y que sea conforme allí funcionará bien, simplemente porque 1000m es muy pequeña. Sin embargo, para radios mucho más grandes, una proyección gnomónica será horrible. Probablemente quisiste estipular una proyección equidistante .
whuber
2
Gran retroalimentación, una proyección AEQ obviamente está funcionando mucho mejor para esta técnica, por lo que he cambiado gnomic. AEQP también aguantará distancias mucho mayores, como en el rango de más de 10,000 km.
Mike T
1
Puede que esté malinterpretando el código, pero solo necesita construir el polígono del búfer una vez, en cualquier proyección AEQD (El centro siempre es cero, la coordinación mínima siempre es -1k, la máxima siempre es + 1k. Luego, desproyecte a lat / lon usando un AEQD se centró en cada uno de los puntos que necesita para obtener los valores lat / lon ...
mkennedy
2
@mkennedy tienes un buen punto. projectedde hecho siempre está en (0, 0) y bufferedtiene los puntos ± 1000 m en las direcciones x e y . Si fuera crítico optimizar esto, simplemente transforme una versión cartesiana simple del búfer del AEQD dinámico a WGS84.
Mike T
4

En lugar de buscar la zona UTM correcta, puede crear una proyección mercator transversal personalizada para cada punto con

+proj=tmerc +lat_0=.... +lon_0=... +k=1 +x_0=0 +y_0=0 +ellps=WGS84 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs

Dibuja el círculo en esa proyección. Las coordenadas del vértice del círculo proyectado siempre serán las mismas, por lo que debe crearlas solo una vez. Para lo siguiente, simplemente asígneles el nuevo CRS personalizado.

Vuelva a proyectar el círculo a EPSG: 4326 para su uso posterior.

Dentro del rango de 1000m, el círculo será casi exacto. Si no (o para círculos más grandes), use en aeqdlugar de tmerc.

AndreJ
fuente
0

¿Qué pasa si adoptas el enfoque de crear un 1000 metros en EPSG: 4326 alrededor de cada uno de tus puntos? Luego convierta el EPSG: 4326 a su otro sistema de coordenadas? La ventaja de proyectar el punto es que no tiene que preocuparse por la curvatura de la tierra con EPSG: 4326.

Greg
fuente
1
¿Cómo exactamente crearía buffers de 1000 m de EPSG: 4326, que tiene unidades de longitud en grados?
Mike T
Una forma de abordar esto es crear un búfer de 1000 metros en EPSG: 32635. Convierta eso a EPSG: 4326 y ahora tendría el número que necesita.
Greg
1
Ese es el mismo enfoque descrito en la pregunta, junto con las limitaciones de esta técnica.
Mike T