Run a geospatial query in memory using Scala - scala

Is there a way to run in Scala a geospatial query, given a set of lat/lon coordinates, to find nearest by distance? The query needs to run in memory possibly.
The set of values is roughly 1 million lon/lat coordinates. I am trying to do that in Spark but the only solution I have found is Magellan but I cannot make it even work for Spark 1.6 and Scala 2.11 so I am trying customized solution.
Example of query: Given one point in wgs84 coordinates and the 1 million set of wsg84 coords, I want the nearest 15 coords in a radius of one mile.

Here is a library with RTree implemetation that can be used for indexing of geo data in Scala: https://github.com/davidmoten/rtree
Just select by bounding box rectangle(s) for your point which will be center of a circle with given radius (distance in your case) and then filter points by the distance to cut out false positives in corners of bounding boxes and then sort results by already calculated distance to take required the nearest 15.
You can use the ‘haversine’ formula to check distance condition between points (see description here http://www.movable-type.co.uk/scripts/latlong.html):
import java.lang.Math._
import com.github.davidmoten.rtree.geometry.{Point, Rectangle}
import com.github.davidmoten.rtree.geometry.Geometries._
def distance(p1: Point, p2: Point): Double = {
val radLon1 = toRadians(p1.x)
val radLat1 = toRadians(p1.y)
val radLon2 = toRadians(p2.x)
val radLat2 = toRadians(p2.y)
val x = sin((radLon2 - radLon1) * 0.5)
val y = sin((radLat2 - radLat1) * 0.5)
val a = y * y + cos(radLat1) * cos(radLat2) * x * x
atan2(sqrt(a), sqrt(1 - a)) * 12756274 // The Earth diameter in meters
}
For calculation of bounding boxes use following function:
def boundingRectangles(c: Point, r: Double): List[Rectangle] = {
val radLon = toRadians(c.x)
val radLat = toRadians(c.y)
val radDist = r / 6378137 // The Earth radius in meters
val lat1 = toDegrees(radLat - radDist)
val lat2 = toDegrees(radLat + radDist)
if (lat1 > -90 && lat2 < 90) {
val deltaLon = asin(sin(radDist) / cos(radLat))
val lon1 = toDegrees(radLon - deltaLon)
val lon2 = toDegrees(radLon + deltaLon)
if (lon1 < -180) rectangle(-180, lat1, lon2, lat2) :: rectangle(lon1 + 360, lat1, 180, lat2) :: Nil
else if (lon2 > 180) rectangle(-180, lat1, lon2 - 360, lat2) :: rectangle(lon1, lat1, 180, lat2) :: Nil
else rectangle(lon1, lat1, lon2, lat2) :: Nil
} else rectangle(-180, max(lat1, -90), 180, min(lat2, 90)) :: Nil
}
List of rectangles required for case when a circle is crossed by the date change meridian, because the RTree doesn't support wrapping of geo-coordinates over the Earth, so we split that rectangles on two by the date change meridian.
Formula and description are here http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#Longitude
EDIT: Finally we ended up to have our own version of the immutable RTree with STR packing that is tuned for efficient window and knn queries on both plane and spherical geometries:
https://github.com/plokhotnyuk/rtree2d

if you want arbitrary datums then you probably need a library but if it is just distance in wgs84 it is a straight forward formula see for example the response to Calculate distance in meters when you know longitude and latitude in java

Related

How get distance in degrees to calculate buffers in athena?

Athena only allows to calculate the distance of the buffer in decimal degrees but this value varies with respect to the latitude in the globe, tate to obtain a distance according to the following formula but it is not consistent in Mexico.
Athena function like this : ST_Buffer(geometry, double)
Athena geospatial functions
So, is posible obtain the corresponding distance in decimal degrees over a custom point in map , ex : get the decimal degree for point x, y like that distance in meters is 300 mts
Currently I use the following formula to approximate the decimal degrees but some buffers are quite horrible although it meets the minimum required
SELECT
ST_Buffer(ST_GeometryFromText( shape_wkt) ,
abs(5000.0 * 360.0 / (2.0 * pi() * cos( latitud )* 6400000.0) ) ) AS
dinamic_buffer_5000
5000 is buffer in meters
6400000.0 earth radius in meters
Some useffull questions :
gps-coordinates-in-degrees-to-calculate-distances
Calculate distance in meters using results in degrees
calculating-latitude-longitude-x-miles-from-point
A possible alternative is the following
To obtain the decimal degrees relative to a point one could:
Generate a second point at a distance d for this you would have to implement this formula, where the bearing does not matter
With this second point calculate the distance in Athena that will return the distance in decimal degrees, as input for the buffer function.
As an approximate is good alternative
Now how implement the second point ?....Here is the formula
I will try to convert to SQL code if can :
After a test I realize that even with the difference of distance it is not possible to obtain the buffer in an optimal way.
In this case the distance to the lower point was 300 meters, after obtaining the distance in decimal degrees with Athena an oblate shape is obtained, it changes the degree of inclination of the point by 90 degrees but it only generates a slightly larger shape.
Destination point given distance and bearing from start point
Source code (zory im edit for test my sql ):
destinationPoint(distance, bearing, radius=6371e3) {
// sinφ2 = sinφ1⋅cosδ + cosφ1⋅sinδ⋅cosθ
// tanΔλ = sinθ⋅sinδ⋅cosφ1 / cosδ−sinφ1⋅sinφ2
// see mathforum.org/library/drmath/view/52049.html for derivation
const dist_ang = distance / radius; // angular distance in radians
const angulo = Number(bearing).toRadians();
const rad_lat = this.lat.toRadians();
const rad_lon = this.lon.toRadians();
console.log("distance", distance);
console.log("radius", radius);
console.log("angular distance in radians", dist_ang);
console.log("bearing", Number(bearing));
console.log("bearing angulo ", angulo );
console.log("lat.toRadians", rad_lat);
console.log("lon.toRadians", rad_lon);
console.log("lon",this.lon);
console.log("lat",this.lat);
const sinφ2 = Math.sin(rad_lat) * Math.cos(dist_ang) + Math.cos(rad_lat) * Math.sin(dist_ang) * Math.cos(angulo);
const φ2 = Math.asin(sinφ2); //lat
console.log("φ2",φ2); //lat
console.log("sinφ2",sinφ2);
const y = Math.sin(angulo) * Math.sin(dist_ang) * Math.cos(rad_lat);
const x = Math.cos(dist_ang) - Math.sin(rad_lat) * sinφ2;
console.log("y",y);
console.log("x",x);
const λ2 = rad_lon + Math.atan2(y, x); //lon
console.log("λ2",λ2);
const lat = φ2.toDegrees();//lat
const lon = λ2.toDegrees();//lon
console.log("lon2",lon);
console.log("lat2",lat);
return new LatLonSpherical(lat, lon);
}

Compute coordinates position with projection

Given 2 coordinates (point 1 and 2 in red) in WGS84 I need to find the coordinates of the point perpendicular (point 3) to the line at a given distance.
I could manage to make the math to compute this perpendicular point, but when displayed on the map, the point seems to be at a wrong place, probably because of the projection.
What I want on a map:
And what I have instead on the map:
How can I take into account the projection so that the point on the map appears perpendicular to the line? The algorithm below to compute the point comes from here: https://math.stackexchange.com/questions/93424/calculate-rectangle-coordinates-from-line-and-height
public static Coords ComputePerpendicularPoint(Coords first, Coords last, double distance)
{
double slope = -(last.Lon.Value - first.Lon.Value) / (last.Lat.Value - first.Lat.Value);
// number of km per degree = ~111km (111.32 in google maps, but range varies between 110.567km at the equator and 111.699km at the poles)
// 1km in degree = 1 / 111.32km = 0.0089
// 1m in degree = 0.0089 / 1000 = 0.0000089
distance = distance * 0.0000089 / 100; //0.0000089 => represents around 1m in wgs84. /100 because distance is in cm
double t = distance / Math.Sqrt(1 + (slope * slope));
Coords perp_coord = new Coords();
perp_coord.Lon = first.Lon + t;
perp_coord.Lat = first.Lat + (t * slope);
return perp_coord;
}
Thank you in advance!

Finding coordinates from heading and distance in R

I want to get the coordinates of new points, preferably using the sf package, when the inital positions and the distance and heading traveled is known.
Consider this; we have three points (pts), with a heading and a distance in km attached. How to find the coordinates for the new positions?
library(data.table, dplyr, sf)
dat <- data.table(lon = c(10,10.1,10.4), lat = c(58.4,57.4,57.8),
heading = c(45,10,235), distance_km = c(1,5.3,3))
pts <- dat %>%
sf::st_as_sf(coords = c("lon","lat")) %>%
sf::st_set_crs(4326)
Simple feature collection with 3 features and 2 fields
Geometry type: POINT
Dimension: XY
Bounding box: xmin: 10 ymin: 57.4 xmax: 10.4 ymax: 58.4
Geodetic CRS: WGS 84
heading distance_km geometry
1 45 1.0 POINT (10 58.4)
2 10 5.3 POINT (10.1 57.4)
3 235 3.0 POINT (10.4 57.8)
Was considering making circles around the points, but dont know how to connect the point to the circle with the correct heading.
buf <- st_buffer(pts, dist = pts$distance_km*1000)
circ <- st_cast(buf, "LINESTRING")
Found the answer here: Calculate coordinate from starting point, having distance and an angle for all quadrants and here:
Convert radians to degree / degree to radians
Will post my R solution for completeness. If anyone has a better or more smooth solution, feel free to post it.
library(data.table, sf, mapview)
dat <- data.table(lon = c(10,10.1,10.4), lat = c(58.4,57.4,57.8),
heading = c(45,10,235), distance_km = c(1,5.3,3))
pts <- dat %>%
sf::st_as_sf(coords = c("lon","lat")) %>%
sf::st_set_crs(4326)
pts <- st_transform(pts, 32632)
pts$utm_n <- st_coordinates(pts)[,1]
pts$utm_e <- st_coordinates(pts)[,2]
buf <- st_buffer(pts, dist = pts$distance_km*1000)
circ <- st_cast(buf, "LINESTRING")
rad2deg <- function(rad) {(rad * 180) / (pi)}
deg2rad <- function(deg) {(deg * pi) / (180)}
pts$newp_e <- pts$utm_e + (pts$distance_km*1000* cos(deg2rad(pts$heading)))
pts$newp_n <- pts$utm_n + (pts$distance_km*1000* sin(deg2rad(pts$heading)))
dt <- data.table(pts)
pts2 <- dt %>%
sf::st_as_sf(coords = c("newp_n", "newp_e")) %>%
sf::st_set_crs(32632)
mapview(pts2) + mapview(pts, zcol = "heading") + mapview(circ)

Find pixel coordinate of world/geographic coordinate in tile

I'm trying to use Mapbox Terrain RGB to get elevation for specific points in space. I used mercantile.tile to get the coordinates of the tile containing my point at zoom level 15, which for -43º, -22º (for simplicity sake) is 12454, 18527, then mercantile.xy to get the corresponding world coordinates: -4806237.7150042495, -2621281.2257876047.
Shouldn't the integer part of -4806237.7150042495 / 256 (tile size) equal the x coordinate of the tile containing the point, that is, 12454? If this calculation checked out I'd figure that I'm looking for the pixel column (x axis) corresponding to the decimal part of the result, like column 127(256 * 0,5) for 12454,5. However, the division results in -18774.366, (which is curiously close to the tile y coordinate, but it looks like a coincidence). What am I missing here?
As an alternative, I thought of using mercantile.bounds, assigning the first and last pixel columns to the westmost and eastmost longitudes, and finding my position with interpolation, but I wanted to check if I'm doing this the right/recommended way. I'm interested in point elevations, so everything said here goes for the Y axis as well.
Here's what I got so far:
def correct_altitude_mode(kml):
with open(kml, "r+") as f:
txt = f.read()
if re.search("(?<=<altitudeMode>)relative(?=<\/altitudeMode>)", txt):
lat = round(float(find_with_re("latitude", txt)), 5)
lng = round(float(find_with_re("longitude", txt)), 5)
alt = round(float(find_with_re("altitude", txt)), 5)
z = 15
tile = mercantile.tile(lng, lat, z)
westmost, southmost, eastmost, northmost = mercantile.bounds(tile)
pixel_column = np.interp(lng, [westmost, eastmost], [0,256])
pixel_row = np.interp(lat, [southmost, northmost], [256, 0])
response = requests.get(f"https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{tile.x}/{tile.y}.pngraw?access_token=pk.eyJ1IjoibWFydGltcGFzc29zIiwiYSI6ImNra3pmN2QxajBiYWUycW55N3E1dG1tcTEifQ.JFKSI85oP7M2gbeUTaUfQQ")
buffer = BytesIO(response.content)
tile_img = png.read_png_int(buffer)
_,R,G,B = (tile_img[int(pixel_row), int(pixel_column)])
print(tile_img[int(pixel_row), int(pixel_column)])
height = -10000 + ((R * 256 * 256 + G * 256 + B) * 0.1)
print(f"R:{R},G:{G},B:{B}\n{height}")
plt.hlines(pixel_row, 0.0, 256.0, colors="r")
plt.vlines(pixel_column, 0.0, 256.0, colors="r")
plt.imshow(tile_img)

How to get data that is all within radius of somewhere, and within the radius I'm looking for?

I am using postgres and postgis.
I have Posts which have a geometry, with an attribute visible_within_m which is how many meters from that point the Post should be shown in results.
I can find Posts within some random radius of some random points by doing ST_DWithin(geometry, ST_SetSRID(ST_Point(a, b), 4326), 10000)
However, I want to know how many Posts are visible with a radius of some random point.
How can I look up how many Posts are visible within a radius of some arbitrary point?
Is there a better way to do this?
You can calculate the distance between each point and the center of your circle. If the distance is grater than the radius then it is outside otherwise it's inside.
const EARTH_RADIUS = 6371000;
const toRad = function(num){return num*Math.PI/180};
var calculateDistance =
function(lat1, lon1, lat2, lon2){
var dLat = toRad(lat2 - lat1);
var dLon = toRad(lon2 - lon1);
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRad(lat1)) *
Math.cos(toRad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
var distance = EARTH_RADIUS * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return distance;
}
Instead of using a constant value for the distance, use the value stored in visible_within_m
SELECT * FROM mytable
WHERE ST_DWithin(geometry, ST_SetSRID(ST_Point(a, b), 4326), visible_within_m);
On a side note, st_dwithin with geometries uses the distance unit of the projection, so for 4326 it is a (meaningless) distance in degrees, not in meters.