MySQL query claims point is not within polygon - mysql

I've drawn a polygon which contains a point in Google Maps. But if I pass the coordinates to MySQL to calculate if the point is within the polygon, it returns false.
SELECT ST_Within(
ST_GeomFromText('POINT(8.34047 54.91320)', 4326),
ST_GeomFromText('POLYGON((62.144619879597 10.486242310988,54.622536815923 2.3124141859883,55.403637023919 23.977453248488,62.144619879597 10.486242310988))', 4326)
) AS is_point_within_polygon;
=> returns 0
But the point is obviously within the polygon:
I double-checked that using Python:
import numpy as np
from shapely.geometry import Point
from shapely.geometry.polygon import Polygon
if __name__ == '__main__':
v0 = [62.144619879597, 10.486242310988]
v1 = [54.622536815923, 2.3124141859883]
v2 = [55.403637023919, 23.977453248488]
lats_vect = np.array([v0[0], v1[0], v2[0]])
lons_vect = np.array([v0[1], v1[1], v2[1]])
lats_vect = np.append(lats_vect, lats_vect[0])
lons_vect = np.append(lons_vect, lons_vect[0])
lons_lats_vect = np.column_stack((lons_vect, lats_vect))
polygon = Polygon(lons_lats_vect)
point = Point(8.34047, 54.9132)
print(point.within(polygon))
=> prints True
What's wrong with the MySQL query?

I think there are two issues here:
First with the query. You list polygon in lat-lon order, but the point seems to be in lon-lat order. You probably want
SELECT ST_Within(
ST_GeomFromText('POINT(54.91320 8.34047)', 4326), -- NOTE CHANGE HERE
ST_GeomFromText('POLYGON((62.144619879597 10.486242310988,54.622536815923 2.3124141859883,55.403637023919 23.977453248488,62.144619879597 10.486242310988))', 4326)
) AS is_point_within_polygon;
Even this query returns FALSE, and this is expected in MySQL. 4326 is Geodesic coordinate system, meaning it operates on the spherical Earth, not on this flat map. With geodesic CRS, edges follow the geodesic shortest lines on Earth, not straight lines on flat map, and for really long lines like here and points close to the edge it matter:
Points slightly further North would be within the polygon, e.g. check out
SELECT ST_Within(
ST_GeomFromText('POINT(56 8.34047)', 4326),
ST_GeomFromText('POLYGON((62.144619879597 10.486242310988,54.622536815923 2.3124141859883,55.403637023919 23.977453248488,62.144619879597 10.486242310988))', 4326)
) AS is_point_within_polygon

Related

How may I calculate the centroid of a multipolygon in shapely (not a polygon)

I understand that the centroid of a polygon may be calculated from
from shapely.geometry import Polygon
coordinate_list = [[1,2], [2,3], [5,5]]
output = Polygon(coordinate_list).centroid
However, my coordinate_list is a multiple polygons, e.g. my coordinate_list = [[[1,2], [2,3], [5,5]], [[0,0], [0,1], [1,0]]]
Is there way to do this. Shapely appears to have a multipolygon class but it does not operate the same as the Polygon class.
You can use MultiPolygon().centroid, it's just that you can't pass that coordinate_list directly to MultiPolygon constructor as it:
/../ takes a sequence of exterior ring and hole list tuples /../
/../ also accepts an unordered sequence of Polygon instances /../
https://shapely.readthedocs.io/en/stable/manual.html#collections-of-polygons
# Based on Multipolygon sample,
# https://shapely.readthedocs.io/en/stable/code/multipolygon.py
from matplotlib import pyplot
from shapely.geometry import Polygon, MultiPolygon
from descartes.patch import PolygonPatch
# from https://github.com/shapely/shapely/blob/main/docs/code/figures.py
from figures import BLUE, BLACK, SIZE, set_limits, plot_coords, color_isvalid
fig = pyplot.figure(1, figsize=SIZE, dpi=90)
ax = fig.add_subplot(121)
set_limits(ax, -1, 6, -1, 6)
coordinate_list = [[[1,2], [2,3], [5,5]], [[0,0], [0,1], [1,0]]]
# "constructor takes a sequence of exterior ring and hole list tuples" -
# https://shapely.readthedocs.io/en/stable/manual.html#collections-of-polygons
multi = MultiPolygon([(coordinate_list[0], []), (coordinate_list[1], [])])
# "the constructor also accepts an unordered sequence of Polygon instances"
#multi = MultiPolygon([Polygon(coordinate_list[0]),Polygon(coordinate_list[1])])
plot_coords(ax, multi.centroid, color=BLACK)
for polygon in multi.geoms:
plot_coords(ax, polygon.exterior)
patch = PolygonPatch(polygon, facecolor=BLUE, edgecolor=BLUE, alpha=0.5, zorder=2)
ax.add_patch(patch)

Getting latitude and longitude of the Sun on a world map with PyEphem

I'm trying to determine the latitude and longitude of say the Sun, the Moon and Mars. I need the result relative to the Earth's equator and the Prime Meridian in order to produce a result similar to this map.
I believe that's also what the author of this question wanted, however the answer there doesn't add up for me (comparing with values from the first link).
Expected result, obtained from the page linked to earlier:
On Thursday, 1 January 2015, 00:00:00 UTC the Sun is at its zenith at Latitude: 23° 02' South, Longitude: 179° 29' West
>>> import ephem; from math import degrees
>>> b = ephem.Sun(epoch='date'); b.compute('2015/1/1 00:00:00')
>>> print("{},{}".format(degrees(b.dec), degrees(b.ra)))
-23.040580418272267,281.12827017399906
So the latitude/declination seems about right, but no 180° wraparound will fix that right ascension, probably because it starts at the Vernal Equinox.
I have also unsuccessfully tried to use an observer at 0,0.
Can this be done using PyEphem, Skyfield or astropy? It seems odd that artificial satellites in PyEphem have the convenient sublat and sublong attributes, but it's so hard for celestial bodies.
I finally figured it out. Sort of. Actually I just ported the relevant bits of libastro to Python. Note that this code runs against the current git version of Skyfield (be6c7296).
Here goes (gist version):
#!/usr/bin/env python3
from datetime import datetime, timezone
from math import atan, atan2, degrees, floor, pi, radians, sin, sqrt
from skyfield.api import earth, JulianDate, now, sun
def earth_latlon(x, y, z, time):
"""
For an object at the given XYZ coordinates relative to the center of
the Earth at the given datetime, returns the latitude and longitude
as it would appear on a world map.
Units for XYZ don't matter.
"""
julian_date = JulianDate(utc=time).tt
# see https://en.wikipedia.org/wiki/Julian_date#Variants
# libastro calls this "mjd", but the "Modified Julian Date" is
# something entirely different
dublin_julian_date = julian_date - 2415020
# the following block closely mirrors libastro, so don't blame me
# if you have no clue what the variables mean or what the magic
# numbers are because I don't either
sidereal_solar = 1.0027379093
sid_day = floor(dublin_julian_date)
t = (sid_day - 0.5) / 36525
sid_reference = (6.6460656 + (2400.051262 * t) + (0.00002581 * (t**2))) / 24
sid_reference -= floor(sid_reference)
lon = 2 * pi * ((dublin_julian_date - sid_day) *
sidereal_solar + sid_reference) - atan2(y, x)
lon = lon % (2 * pi)
lon -= pi
lat = atan(z / sqrt(x**2 + y**2))
return degrees(lat), degrees(-lon)
if __name__ == '__main__':
print("2015-01-01 00:00:00:")
time = datetime(2015, 1, 1, tzinfo=timezone.utc)
x, y, z = earth(JulianDate(utc=time)).observe(sun).apparent().position.au
print(earth_latlon(x, y, z, time))
print("now:")
time = datetime.now(timezone.utc)
x, y, z = earth(JulianDate(utc=time)).observe(sun).apparent().position.au
print(earth_latlon(x, y, z, time))
Output:
2015-01-01 00:00:00:
(-23.05923949080624, -179.2173856294249)
now:
(-8.384551051991025, -47.12917634395421)
As you can see, the values for 2015-01-01 00:00:00 match the reference values from the question. Not precisely, but it's good enough for me. For all I know, my values might be better.
Due to my ignorance about the undocumented magic numbers used in the libastro code, I cannot make this work for bodies other than Earth.
#BrandonRhodes: Let me know if you're interested in having this functionality in Skyfield, then I'll try to throw together a pull request.

SQL Server 2012 geography STIntersects understanding

I am trying to get all the points inside the polygon and after a few tests I finally caught.
STIntersects always return 1 for all my entities even in cases when a point "visually" not in a polygon.
I have found posts about polygon creation direction and try it but it didnt't help me :)
So I've decided to create simple case:
polygon with 4 vertexes somewhere in Russia (corners: 55 37, 56 38)
one point inside this polygon
and another one outside
here is a link to google map with points
and results of this test knocked me out of a rut
declare #cw geography, #ccw geography, #pointIn geography, #pointOut geography
-- counterclockwise direction from bottom left corner
set #ccw = geography::STPolyFromText (
'POLYGON((
55.0 37.0
,55.0 38.0
,56.0 38.0
,56.0 37.0
,55.0 37.0
))', 4326
)
-- clockwise direction from bottom left corner
set #cw = geography::STPolyFromText (
'POLYGON((
55.0 37.0
,56.0 37.0
,56.0 38.0
,55.0 38.0
,55.0 37.0
))', 4326
)
set #pointIn = geography::Point(55.5, 37.5, 4326)
set #pointOut = geography::Point(54, 36, 4326)
select #pointIn.STIntersects(#ccw) ccw, #pointIn.STIntersects(#cw) cw
-- result: 1 0
--here i should get inversed values, but it didnt happens
select #pointOut.STIntersects(#ccw) ccw, #pointOut.STIntersects(#cw) cw
-- result: 1 0
Why does this happen? I just can't understand what I am missing
I expect that the pointIn should return 1 when my polygon is small and return 0 when my polygon is whole world minus selected area, and pointOut should return 0 in first case and return 1 in the second one
But both points return 1 in counterclockwise polygon.
UPDATE
finaly
My error was in the order of the input parameters of geography::STPolyFromText
first should be lng and the second lat.
and in extension the order is diferent
msdn says: Point ( Lat, Long, SRID )
In SqlGeography instances, #ccw defines a hole (due to it's counter-clockwise order). As this hole is not within a polygon, it becomes a hole in the Globe. Put another way, you get a Polygon covering the entire globe, minus your #cw polygon.
Geography::Point also uses reverse coordinates to WKT and therefore I believe your latitude and longitude coordinates are the wrong way round (impossible to be absolutely sure) as only you know your data. Regardless, the attached image should enough to explain this. The "Orange" colour being #ccw, the blue being #cw, the rest is labelled.
If you have SSMS you can visualise it using the following query, note the points are buffered so you can see them.
select
'Clockwise' AS Label,
#cw AS Item
union all
select
'Counter-Clockwise' AS Label,
#ccw AS Item
union all
select
'Point In' AS Label,
#pointIn.STBuffer(100000) AS Item
union all
select
'Point Out' AS Label,
#pointOut.STBuffer(100000) AS Item
Hope it helps.

DbGeography.Distance() returning incorrect distance

I'm experimenting with System.Data.Spatial.DbGeography, that I want to use to determine the distance from one coordinate to another (going to be stored in SQL server).
My coordinates are in lat/long, and I got them from Bing Maps (I've tried with coordinates from Google Maps too, with the same result).
var osloCentralStation = DbGeography.FromText("POINT(59.9109 10.7523)", 4326);
var drammen = DbGeography.FromText("POINT(59.7378 10.2050)", 4326);
Console.WriteLine("Distance: {0}km", osloCentralStation.Distance(drammen) / 1000);
Returns:
Distance: 63,4340839088124km
The returned distance is approximately double what it should be.
https://maps.google.com/maps?saddr=59.9109+10.7523&daddr=59.7378+10.2050
Does anybody have any idea as to what's going on?
You're not declaring the element in WKT in the right order.
WKT should be in your case:
POINT(10.2050 59.7378)
See OGC standard here:
http://msdn.microsoft.com/en-us/library/bb933834.aspx
http://en.wikipedia.org/wiki/Well-known_text
And then it has to be declared like:
POINT(LONGITUDE LATITUDE)
Also keep in mind that it won't be the driving distance but the distance by air.
It turns out that lat/long are given as long/lat when creating new DbGeography objects.
I've written a little helper method so that I don't get it wrong again in the future:
private static DbGeography CreateDbGeography(double latitude, double longitude, int srid = 0)
{
var text = string.Format(CultureInfo.InvariantCulture.NumberFormat, "POINT({0} {1})", longitude, latitude);
if (srid > 0)
{
return DbGeography.FromText(text, srid);
}
return DbGeography.FromText(text);
}

Constructing a triangle based on Coordinates on a map

I'm constructing a geolocation based application and I'm trying to figure out a way to make my application realise when a user is facing the direction of the given location (a particular long / lat co-ord). I've got the math figured, I just have the triangle to construct.
//UPDATE
So I've figured out a good bit of this...
Below is a method which takes in a long / lat value and attempts to compute a triangle finding a point 700 meters away and one to its left + right. It'd then use these to construct the triangle. It computes the correct longitude but the latitude ends up somewhere off the coast of east Africa. (I'm in Ireland!).
public void drawtri(double currlng,double currlat, double bearing){
bearing = (bearing < 0 ? -bearing : bearing);
System.out.println("RUNNING THE DRAW TRIANGLE METHOD!!!!!");
System.out.println("CURRENT LNG" + currlng);
System.out.println("CURRENT LAT" + currlat);
System.out.println("CURRENT BEARING" + bearing);
//Find point X(x,y)
double distance = 0.7; //700 meters.
double R = 6371.0; //The radius of the earth.
//Finding X's y value.
Math.toRadians(currlng);
Math.toRadians(currlat);
Math.toRadians(bearing);
distance = distance/R;
Global.Alat = Math.asin(Math.sin(currlat)*Math.cos(distance)+
Math.cos(currlat)*Math.sin(distance)*Math.cos(bearing));
System.out.println("CURRENT ALAT!!: " + Global.Alat);
//Finding X's x value.
Global.Alng = currlng + Math.atan2(Math.sin(bearing)*Math.sin(distance)
*Math.cos(currlat), Math.cos(distance)-Math.sin(currlat)*Math.sin(Global.Alat));
Math.toDegrees(Global.Alat);
Math.toDegrees(Global.Alng);
//Co-ord of Point B(x,y)
// Note: Lng = X axis, Lat = Y axis.
Global.Blat = Global.Alat+ 00.007931;
Global.Blng = Global.Alng;
//Co-ord of Point C(x,y)
Global.Clat = Global.Alat - 00.007931;
Global.Clng = Global.Alng;
}
From debugging I've determined the problem lies with the computation of the latitude done here..
Global.Alat = Math.asin(Math.sin(currlat)*Math.cos(distance)+
Math.cos(currlat)*Math.sin(distance)*Math.cos(bearing));
I have no idea why though and don't know how to fix it. I got the formula from this site..
http://www.movable-type.co.uk/scripts/latlong.html
It appears correct and I've tested multiple things...
I've tried converting to Radians then post computations back to degrees, etc. etc.
Anyone got any ideas how to fix this method so that it will map the triangle ONLY 700 meters in from my current location in the direction that I am facing?
Thanks,
for long distance: http://www.dtcenter.org/met/users/docs/write_ups/gc_simple.pdf
but for short distance You can try simple 2d math to simulate "classic" compass using: http://en.wikipedia.org/wiki/Compass#Using_a_compass. For example you can get pixel coordinates from points A and B and find angle between line connecting those points and vertical line.
also You probably should consider magnetic declination: http://www.ngdc.noaa.gov/geomagmodels/Declination.jsp
//edit:
I was trying to give intuitive solution. However calculating screen coordinates from long/lat wouldn't be easy so You probably should use formulas provided in links.
Maybe its because I don't know javascript, but don't you have to do something like
currlat = Math.toRadians(currlat);
to actually change the currlat value to be radians.
Problem was no matter what I piped in java would output in Radians, Trick was to change everything to Radians and then output came in radians, convert to degrees.