ol3 - calculate real meters in epgs 3857 - javascript

I use ol3 as a map service.
I have made a linestring with two points with coordinates : [[0,0],[0,1000]] and calculated the distance using the vincenty formula. This resulted in 1000 meter, what is accurate.
But when i made another linestring for example [[4052627,3971934],[4052627,3972934]] vincenty distance was around 850 meters.
I dont know what i forgot here. Any way to correct that? I want to calculate epgs:3857 meters (units) for a given real distance.

You can use ol.sphere.haversineDistance:
var c1 = [4052627, 3971934];
var c2 = [4052627,3972934];
var wgs84Sphere = new ol.Sphere(6378137);
var length = wgs84Sphere.haversineDistance(
ol.proj.transform(c1, 'EPSG:3857', 'EPSG:4326'),
ol.proj.transform(c2, 'EPSG:3857', 'EPSG:4326'));
// 833.12 m

Distances are tricky. The fact that the map units of a coordinate system are in meters (as epsg:3857) doesn't mean that you can measure distances in meters directly. See https://en.wikipedia.org/wiki/List_of_map_projections, and look how many of those have the "equidistant" property.
I suggest you use turf.js to calculate accurate geodetic distances: http://turfjs.org/static/docs/module-turf_distance.html

After alot of search I've found this PDF document:
Web Mercator:
Non-Conformal, Non-Mercator
There is something called Point Scale Factor - according to the way web Mercator is projected - which has tow values, not one like normal Mercator , North/South Scale Factor and East/West Scale Factor.
in my program I've ignored the East/West Scale Factors because it's to much small.
Once I've calculated the scale factor the real distance is almost equal to scale_factor * epgs_3857_distence

Related

D3 Topojson Circle with Radius Scaled in Miles

(assuming existing projection/topojson)
What I'm trying to do is create a circle at a point ([long,lat]) of radius (r) in miles. I know there is a d3.geo function for this, but after some consideration I don't think it will be very compatible with my particular application.
So now I'm looking for using a native svg circle solution, where cx and cy are the lat and long, and r is the radius in miles. I know the cx and cy, but I don't know how to make sure the r is say 15 miles. So the main thing is how to make sure the radius is scaled in miles when drawn in pixel space. There must be someway to use the projection function to set the appropriate scale for the radius. But I haven't seen this in practice.
Also I should point out that my projection is dynamic, depending on user events the projection (including scale) can change. So I'm not sure if that will have bearing on how circles are scaled within the context of an existing projection, but I thought I would disclose that to be on the safe side.
Why not use the built-in circle generator d3.geoCircle()?
Returns a new GeoJSON geometry object of type “Polygon” approximating a circle on the surface of a sphere, with the current center, radius and precision. Any arguments are passed to the accessors.
The only task left to you is to calculate the radius of the circle in degrees. Because earth is not a perfect sphere this can become quite challenge of its own. But for many applications an approximation will suffice. Taking just the mean radius of 3,958 mi into account, the calculations can be written as:
var EARTH_RADIUS = 3959; // mean radius in miles
var radiusMi = 5; // radius to be drawn in miles
var radiusDeg = radiusMi / EARTH_RADIUS * 90; // radius in degrees for circle generator
This can then be passed to the circle generator:
var circle = d3.geoCircle().radius(radiusDeg);
Finally, the circle generator is used to pass its output via data binding to an appropriate path generator taking into account the projection:
svg.append("path")
.datum(circle)
.attr("d", path);
Have a look at this Block which features circles of 50 miles radius each at various positions around the globe. The circle generator in combination with the projection will take control of the correct sizing and the correct appearance of the circle.
D3 v3
If you are still stuck to D3 v3 the example works as well. However, you need to adjust the names accordingly:
d3.geo.circle ↦ d3.geoCircle
In addition to that, some of the circle generator's methods have been renamed:
circle.origin() ↦ circle.center()
circle.angle() ↦ circle.radius()
Applying those adjustments to my above linked Block, this works for v3 just as well: v3 Block.
This approach gets to play to its strengths when it comes to unusual projections having severe distortions. Just by changing the projection in the Block to d3.geoGnomonic() this becomes easily visible. The following screenshot from the updated Block still shows the same circles as above having a radius of 50 miles each:

leaflet - Calculate a rectangle coordinates from two midline points

I have a LineString geometry feature with weight in GeoJSON. Now I want to determine coordinates of 4 vertices of that rectangle. I know how to calculate on plane geometry by using line equation. But on geographic geometry, I'm new. I already read how to calculate one unit of latitude and longitude decimal degree as well as metres per pixel but I'm still confused how to add them to a given point to calculate wanted points. My problem is like this:
Given:
Broadth of rectangle
Coordinates of two points making the midline,
distance between these two points are equal to the width
Result: Coordinates of 4 vertices of rectangle
For example: Two point of LineString
A (10.767008,106.665884)
B (10.767715,106.667151)
Weight of LineString is 6 pixels at zoom level 18
I can calculate width of rectangle (distance between these two points), broadth of rectangle (number of metres per pixel, then multiply). How can I use them to calculate 4 vertices' coordinates of this rectangle?
All of my calculated cases are on small scale (part of one street of a city) so I think altitude can be excluded
It sounds like you want to get the "extent" of your linestring (a.k.a. the "bounding box" of your linestring). You can use turf.js' turf-extent function to get the extent of your GeoJSON linestring. That extent will be the rectangle you're looking for, as a GeoJSON polygon. You can then use other turf.js functions on that polygon to get the width, length, area, etc

Query database values based on user's location

How can I perform a query on the database as per the user's location value? The application was developed with HTML5, CSS, Javascript, PHP has a database with columns as in the below table.
On the html webpage the users geo coordinates are collected and are to be compared with the values in the database to find the nearest place to the user with the places in the database.
Please let me know how to achieve this. Any examples / samples will be appreciated.
There is a question that compares the capabilities of various spatial databases, GIS: PostGIS/PostgreSQL vs. MySql vs. SQL Server?, where Postgis comes out a pretty clear winner over MySQL.
Whether you use MySQL or Postgis, you would be much better off, if you can, storing your latitude and longitude values as a geometry/geography (Point), as the functions that can be used to find things nearby, ST_Distance, ST_Distance_Sphere and the more obscure <-> operator, see Find n Nearest Neighbors for given Point using PostGIS? (for example usage) work directly on geometry/geography columns. Even more importantly, you can add a spatial index, which these functions need to work properly, which will outperform searches on separately indexed latitude and longitude columns by a large margin (this will depend on table size, but will grow as table size grows),
In Postgis, you can convert lat and lon to a geometry with:
alter table mytable add column geom (Geometry, 4326);
update mytable set geom = ST_SetSRID(ST_MakePoint(lon, lat), 4326)
create index ix_spatial_mytable_geom on mytable using gist(geom);
At this point, you will be able to very efficient queries to find points near other points, using any of the examples in the above links.
You can do similar things in MySQL, although, it does not support a spatial reference system, ie, the 4326 above, which means lat/lon, and it lacks a ST_MakePoint function, so you would need to use STGeomFromText and concatenate the lat/lon together to make a POINT. It also does everything in planar coordinates, as Claudio and others have stated, which is not an issue with Postgis.
I apologize for a long and somewhat tangential answer, but having done various migrations between databases on large amounts of data (MySQL, SQL Server and Postgres/GIS) and made lots of mistakes on the way, I hope I can set you off in the right direction (and add a bit of future proofing, if you want to start using some other spatial functionality, which Postigs has in spades).
For a rough measure I would try something like the following (only Euclidean geometry, it doesn't take into account the Earth curvature or problems like this).
First you could compute the difference between the user's coordinates and the coordinates of places in the database. Like this:
distLat = abs(userLat - placeLat)
distLong = abs(userLong - placeLong)
Then I would compute the distance between the two points using Pythagora's theorem. So:
distance = squareRoot(distLat * distLat + distLong * distLong)
You can compare the distances of all places in the database and take the minimum, which teoreticaly is the place nearest to the user's position.
If you use MySQL I think that a query like this should work:
SELECT * FROM places ORDER BY MIN(SQRT((p.latitude - userLatitude) * (p.latitude - userLatitude) + (p.longitude - userLongitude) * (p.longitude - userLongitude))) LIMIT 1
Beware that this query could be very slow depending on how many places you have, because it needs to read all the rows in the table and compute the distance for each one. Indexes have no effects.
Anyway, for this kind of problems you should better use GIS or databases with good geospatial extensions. MySQL geospatial extension is not very powerful, even in MySQL 5.6. It has a ST_DISTANCE function but still uses Euclidean geometry which is not very accurate for calculation on a spherical surface like the Earth. Anyway, if you use MySQL 5.6, I think that it should be better to use the ST_DISTANCE function, which is for sure much more optimized than doing calculations "manually" in the query.
See also this article for a deep explanation and more examples: http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc
EDIT
As requested by the OP, I add more details on how to deal with negative coordinates.
Negative coordinates are generally not a big issue. I will try to explain this with some examples.
Take for example the coordinates of the Brooklyn bridge: 40.704391, -73.994675.
With the above formula, the distance of the Brooklyn bridge from the Statue of Liberty is:
sqrt((40.704391 - 40.689167)^2 + (-73.994675 - -74.044444)^2) = 0.052045399
Now consider the distance between the Statue of Liberty and the Brooklyn Bowl (coordinates 40.7179666, -73.9670125), that is:
sqrt((40.7179666 - 40.689167)^2 + (-73.9670125 - -74.044444)^2) = 0.082613886
As you can see the distance of the Brooklyn Bowl from the Statue of Liberty is bigger than the distance of the Brooklyn Bridge. It is correct since the Brooklyn Bowl is 4 miles away from the Statue of Liberty, while the Brooklyn Bridge is only 1 mile away from it.
In this example both the two points has a negative longitude. But the formula works even if one of the two has positive coordinates. For example, the distance between the Statue of Liberty and the Tour Eiffel (Paris, coordinates 48.858360, 2.294460), is:
sqrt((48.858360 - 40.689167)^2 + (2.294460 - -74.044444)^2) = 76.77476134
Then calculate also the distance between the Statue of Liberty and the Colosseum (Rome, coordinates 41.890238, 12.492242):
sqrt((41.890238 - 40.689167)^2 + (12.492242 - -74.044444)^2) = 86.54502063
As you can see it works, the distance of the Colosseum is bigger since it is about 8000km away from the Statue of Liberty, while the Tour Eiffel is about 800 km closer.
The only issue I see is when you have to calculate the distance between two places that are in the far east and in the far west respectively. The above formula will give you a very high distance, but actually the they could be very close. Take for example Anchorage, a city in Alaska (coordinates 61.252240, -149.896769), and Beringovskij, a city in the very east of Russia (coordinates 63.049797, 179.310011). They have a distance of only about 1500 km but with the above formula you get:
sqrt((61.252240 - 63.049797)^2 + (-149.896769 - 179.310011)^2) = 329.2116875
Definitely a too high value for only 1500 km: I would expect something less than 50.
The problem is that the formula calculates the distance taking the central meridian as a reference point, that is the meridian with a 0 degrees latitude. This is good until the distance is "no more that half the Earth".
I think that a solution could be to calculate two distances.
The first with a reference point of 0 degrees: it is what the above formula does.
The second with a reference point of 180 degrees. It's like calculating the distance on a world map shifted by 180 degrees, like this: http://www.bouwman.com/world/Formilab-180.html.
And then take the minimum of these two distances.
Thus the formula becomes a little more complex:
distance = squareRoot(min((userLat - placeLat)^2, (userLat - placeLat - 360)^2) + (userLong - placeLong)^2)
Note that we subtract 360 because it is the distance between the degree -180 and the degree 180.
With this new formula we get correct results for places that are more then 180 degrees away from each other, and we get also the same result given by the previous formula when comparing places that are less then 180 degrees away from each other. The calculate distance Anchorage - Beringovskij is now: 30.84564166.
Of course, as I have already said, this is not an exact method for calculating distances. You can take a look at this article for more "scientific" techniques: https://en.wikipedia.org/wiki/Geographical_distance :D

How to check if a given marker is inside a area

Hello everybody o/
I know that this is more a math question than gmap, but I suppose that someone already pass through this =)
In my map, I have circle (actually I have several of them, but this not change the question), like this: http://code.google.com/intl/pt-BR/apis/maps/articles/mvcfun/step6.html
How do I know if a marker (with latitude X and longitude Y) is inside this circle?
Sorry for the bad english, I'm brazillian =p
In Google Maps JavaScript API v3 you can use geometry library. To enable it you have to slightly change the script URL:
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false&libraries=geometry"></script>
The library contains utility functions for the computation of geometric data on sphere. You can utilize it to compute the distance of two points given by their latLngs this way:
var distanceInMetres = google.maps.geometry.spherical.computeDistanceBetween(latLngCircleCenter, latLngPoint);
Now you can easily check if the point is inside the circle (suppose R is in metres):
if(distanceInMetres < R)
alert("in the circle");
If (lat1, lon1) and (lat2, lon2) are your two points and R is the radius of the circle around your first point, then the distance between the points is given by the haversine formula (or the Great-circle distance). But I believe that for your problem, the angles are small enough to use this approximation:
and then check whether d^2 is less than the radius R^2.
But if your latitude and longitude differences are larger than a few degrees, you'll want to use the full haversine formula.
I Recommend you read http://www.movable-type.co.uk/scripts/latlong.html. It provides a number of algorithms for computations of this kind. It includes JavaScript code for the computations.
Basically if you have the coords of circle center (cX,cY) and radius R, and some marker at X,Y you can do the following calculations:
var distanceQuad = (X-cX)*(X-cX)+(Y-cY)*(Y-cY)
if (distanceQuad<=(R*R))
{
alert("Marker inside circle!");
}
This is from trigonometry. You calculate distance as sqrt(sqr(deltaX)+sqr(deltaY)) and compare it with circle Radius. Given code is a bit optimized to get rid of calculating square root.
It's much easier than you'd expect. Read this answer including a working jsFiddle example.

GPS coordinates: 1km square around a point

I was hoping someone out there could provide me with an equation to calculate a 1km square (X from a.aaa to b.bbb, Y from c.ccc to c.ccc) around a given point, say lat = 53.38292839 and lon = -6.1843984? I'll also need 2km, 5km and 10km squares around a point.
I've tried googling around to no avail... It's late at night and was hoping someone might have quick fix handy before I delve into the trigonometry...
I'll be running all this in Javascript, although any language is fine.
If the world were a perfect sphere, according to basic trigonometry...
Degrees of latitude have the same linear distance anywhere in the world, because all lines of latitude are the same size. So 1 degree of latitude is equal to 1/360th of the circumference of the Earth, which is 1/360th of 40,075 km.
The length of a lines of longitude depends on the latitude. The line of longitude at latitude l will be cos(l)*40,075 km. One degree of longitude will be 1/360th of that.
So you can work backwards from that. Assuming you want something very close to one square kilometre, you'll want 1 * (360/40075) = 0.008983 degrees of latitude.
At your example latitude of 53.38292839, the line of longitude will be cos(53.38292839)*40075 = [approx] 23903.297 km long. So 1 km is 1 * (360/23903.297) = 0.015060 degrees.
In reality the Earth isn't a perfect sphere, it's fatter at the equator. And the above gives a really good answer for most of the useful area of the world, but is prone to go a little odd near the poles (where rectangles in long/lat stop looking anything like rectangles on the globe). If you were on the equator, for example, the hypothetical line of longitude is 0 km long. So how you'd deal with a need to count degrees on that will depend on why you want the numbers.
Here is something from my notes to be used on Android with its decimal GPS.
Lat Long:
NY City 40N 47 73W 58 40.783333 73.966667
Wash DC 38N 53 77W 02 38.883333 77.033333
yields = 209 miles !! VERY CLOSE
Distance (miles) (x) = 69.1 (lat2-lat1)
Distance(miles) (y) = 53.0 (long2 - long1)
As crow flys sqrt (x2 + y2) ... duh!#
delta(LAT) / Mile = .014472
delta(LONG) / Mile = .018519
Using a box as approximation
To find someone within 100 miles (100 north / 100 south, 100 E / 100 W)
From 0,0
-14.472 / + 14.472 , -18.519 / 18.519
A simpler way of generating a gps square given the centre would be to use the indirect Vincenty algorithm.The Javascript code here shows how to do it http://www.movable-type.co.uk/scripts/latlong.html.
Creating a square using a circle isn't to hard. Squares are equal distance to each point. So given a centre point, distance from the centre, change the bearing from 0 or any number depending on rotation of the square and increment by 90 degrees or PI/2 radians. By incrementing by 90 degrees each time and you will up with a square in circular space.
I use this myself for generating GPS points around a centre point with a given distance
.---.
--/-
--0--
-/--
.---.
TL;DR
10 km = 0.08999 radius from a certain geopoint. This calculation is only based on latitude values and applies only to geopoints with WGS84 projection.
More details
If you want a more accurate answer you must have to calculate it by building a function of some sort. However it still don't guarantee because people even quarrel for the degrees of error. Taking altitude into account, mercator or not, etc.
Caution
The value above is just a rule of a thumb so don not use it for critical applications.
Reference
GIS StackExchange, How do I calculate the bounding box for given a distance and latitude/longitude, answer by David the Australian developer

Categories