Retrieving JSON data from the osmbuildings.org data site - json

I am trying to retrieve building data from osmbuildings.org
The osmbuildings documentation (https://osmbuildings.org/documentation/data/) features an example URL (https://data.osmbuildings.org/0.2/anonymous/tile/15/17605/10743.json) that returns the JSON file for buildings that are located in Berlin at roughly 52°32'30.6"N 13°25'23.2"E coordinates.
Can I use this same site to get a JSON file for buildings in a different location? For example, I believe the X,Y coordinates using XYZ tile coordinates would be 25835 and 5221 for a location in Singapore for a zoom of 15 and the corresponding url would be https://data.osmbuildings.org/0.2/anonymous/tile/15/25835/5221.json.
However, when I put this into the web browser, I don't get the json file like in the Berlin case. Can someone please explain?
I also tried different zoom values at the same location:
https://data.osmbuildings.org/0.2/anonymous/tile/16/51672/8779.json
And at a slightly different location with fewer buildings:
https://data.osmbuildings.org/0.2/anonymous/tile/16/51666/9459.json

If you do not get the JSON file, you are probably getting an empty reply. This is likely because your specified tile does not contain any building data.
This can have various reasons, e.g. there are regions where no building data is available. In your case however, it seems to me that your conversion into X,Y coordinates is not correct for Singapore.
In my following example, I use the coordinates for the Singapore Marina Bay (https://www.openstreetmap.org/#map=15/1.2742/103.8617).
I converted the lon/lat to tile numbers using the formula from the OSM wiki (https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames). In pseudo code:
n = 2 ^ zoom
xtile = n * ((lon_deg + 180) / 360)
ytile = n * (1 - (ln(tan(lat_rad) + sec(lat_rad)) / π)) / 2
= n * (1 - (ln(tan(lat_rad) + (1 / cos(lat_rad))) / π)) / 2
Filling in the according lon = 103.8617 and lat = 1.2742 and zoom = 15, you get:
n = 2^15
= 32768
xtile = 32768 * ((103.8617 + 180) / 360)
= 25837.722737778
ytile = 32768 * (1 - (ln(tan(1.2742) + (1 / cos(1.2742))) / π)) / 2
= 16268.009923134
Disregarding the decimals, we get X = 25837 and Y = 16268. Making this into a link (according to https://osmbuildings.org/documentation/data/), we get
https://data.osmbuildings.org/0.2/anonymous/tile/15/25837/16268.json
which does return JSON data for some 40 buildings.
This also worked fine for me on other zoom levels. E.g. zooming in on the famous "Marina Bay Sands" (https://www.openstreetmap.org/#map=17/1.28338/103.86148) and calculating the link according to the pseudo-code above, I get:
https://data.osmbuildings.org/0.2/anonymous/tile/17/103350.810851556/65068.69652388.json
which returns a JSON that contains named buildings such as "Marina Bay Sands Tower 1", "Marina Bay Sands Tower 2", etc., which shows it worked as intended.

Related

Trying to find the longitude to metres ratio given a latitude for Google Maps Static API Stitching

I'm working on a project that hopes to convert Google maps static images into larger, stitched together maps.
I have created an algorithm that given a starting and ending, latitude and longitude, it will fetch the Google Maps Static image for that lat,lng pair, then increment lng by the pixel width of the fetched image, in this case 700px, using an algorithm for determining pixel to longitude ratio using a couple of formulas.
The formulas seem to be working perfectly for latitude, as you can see in my attachment, the vertically tiling images line up almost perfectly.
Yet horizontally, the longitude increment seems to be off by a factor of 2, or slightly more.
To increment latitude, I use a constant metres to latitude ratio
const metresToLatRatio = 0.001 / 111 // Get lat value from metres
const metresPerPxLat = getMetresPerPxLng(currentLat, zoom)
const latIncrement = metresToLatRatio * (metresPerPxLat * 700)
But for longitude, I replaces the metresToLngRatio constant with a variable derived from this formula
const metresToLngRatio = getMetresToLngRatio(currentLat)
const metresPerPxLng = getMetresPerPxLng(currentLat, zoom)
lngIncrement = (metresToLngRatio * (metresPerPxLng * 700))
Where getMetresToLngRatio and getMetresPerPxLng are
function getMetresPerPxLng(lat, zoom = 19, scale = 2) {
return Math.abs((156543.03392 * Math.cos(lat * Math.PI / 180) / (2 ** zoom)) / scale)
}
function getMetresToLngRatio(lat) {
return 1 / Math.abs(111111 * Math.cos(lat))
}
The getMetresPerPxLng function is derived from this post and this answer: https://groups.google.com/g/google-maps-js-api-v3/c/hDRO4oHVSeM / https://gis.stackexchange.com/questions/7430/what-ratio-scales-do-google-maps-zoom-levels-correspond-to
What I noticed is that if I change getMetresToLng Ratio to return (1 / Math.abs(111111 * Math.cos(lat))) * 2, the stitching appears more accurate, only off by a few tens of pixels, instead of almost half the image.
With * 2
Without * 2
Am I doing something wrong with my longitude equation? I know that 111111*cos(lat) is a rough estimate, so I'm wondering if there's a more accurate formula
You are using the wrong earth radius. From the answer in https://gis.stackexchange.com/questions/7430/what-ratio-scales-do-google-maps-zoom-levels-correspond-to, you need to update the equation using a radius of 6371010 instead of 6378137 meters.
Please note that stitching tiles together for some other use is likely a violation of the Terms of Service.

calculate walking distance between gps points

I have pairs of gps coordinates (longitude latitude) and I would like to calculate the walking distance between them. i.e. using road data (from google maps or another open source) calculate the km of the shortest route between the two gps points. I could do it using google maps, but I have thousands of pairs so I would like to find a more automated way.
Does somebody know how to do it?
I am not quite sure about what you looking for. Just share some thoughts here:
1) If you want to calculate great circle distance between two points in lat/lon, you could use haversine formula distance. Example in JS:
function Haversine_distance(lat1,lon1,lat2,lon2) {
var R = 6371; // in km
var x1 = lat2 - lat1;
var dLat = x1.toRad();
var x2 = lon2 - lon1;
var dLon = x2.toRad();
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
return d*1.0;
};
2) If you need more accurate distance calculation you need some correction factor, since earth is not a perfer sphere. It is always easier to project the locations you have to an appropriate projection and calculate distance there. For instance, project to UTM zones using proj4js, then calculate the distance to reduce the inaccuracy.
3) If you are talking about walking distance in cities, then it is network distance. It is required to have your road network build up first, then calculate from there. Without the road network, giving only point locations will not be enough to calculate the walking distance. Commercial data for road network is available from such as TeleAtlas. Free data can also be found via OpenStreetMap.

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.

GPS Latitude and longitude from hexadecimal value

I'm getting the following replies from a GPS to a microcontroller, rather than try parse the string and convert that to hex to send, I'd rather just use the hex value supplied to then send to my server but I'm having difficulty working out what format it's stored in.
Lat: 37 Deg 39 Min 48.84 Sec S (0xFF94DE3A)
Lon: 144 Deg 58 Min 10.30 Sec E (0x019C5B97)
(another example:)
Lat: 49 Deg 10 Min 21.49 Sec N (0x008BDE6C)
Lon: 123 Deg 4 Min 14.76 Sec W (0xFEA1EE9A)
I've tried answers to other questions with similar titles to no avail..
Lat in the first example looks like -7020998 dec since it's south, similar to Lon in the second example (-22942054) is also negative in the west.
I've tried dividing the numbers by the converted expected result (using degrees + minutes/60 + seconds/3600) I come up with a rough number,
ie:
0xFF94DE3A = -7020998 / -37.66357 = ~186413.502
0x019C5B97 = 27024279 / 144.96953 = ~186413.510
0x008BDE6C = 9166444 / 49.17264 = ~186413.501
0xFEA1EE9A = -22942054 / -123.07077 = ~186413.508
This looks close to 180000, but if I divide by that it doesn't look right (-7020998 / 180000 = -39.00554). What does 186413 relate to though? I feel like I'm missing something completely obvious..
Edit: I'm able to get it working using the below quick example (I know it's not pretty):
if ($GPSLatitude > 0x7FFFFFFF) // ensure correct signedness
$GPSLatitude-=0x100000000;
$GPSf = floatval($GPSLatitude) / 186413.51334561207757602506827277;
$GPSD = floor($GPSf);
$GPSt = abs($GPSf - $GPSD) * 60;
$GPSM = floor($GPSt);
$GPSS = floor(($GPSt - $GPSM) * 6000) / 100;
echo 'GPS Latitude: ' . $GPSD . ' Deg ' . $GPSM . ' Min ' . number_format($GPSS,2,'.','') . ' Sec';
The results match perfectly, but I know there's got to be a better way than dividing by that horrible number?
According to http://en.wikipedia.org/wiki/Geotagging, the GPS coordinates could be set even in rationals:
When stored in EXIF, the coordinates are represented as a series of
rational numbers in the GPS sub-IFD. Here is a hexadecimal dump of the
relevant section of the EXIF metadata (with big-endian byte order):
I am afraid, you have not all hex digits and you see only the upper part of the rational number. And the invisible divisor could be that magic number.
I am not insisting it is so, I only demonstrate there could be ANYTHING hidden in the format.
And you are counting in degrees, and the coord could simply be gaussian X,Y.
Anyway, what is the name of your GPS format?

Function for line between 2 GPS coordinates

I'm trying to find a function lng = f(lat) that would help me draw a line between 2 given GPS coordinates, (lat1, lng1) and (lat2, lng2).
I've tried the traditional Cartesian formula y=mx+b where m=(y2-y1)/(x2-x1), but GPS coordinates don't seem to behave that way.
What would be a formula/algorithm that could help me achieve my goal.
PS: I'm using Google Maps API but let's keep this implementation agnostic if possible.
UPDATE: My implementation was wrong and it seems the algorithm is actually working as stated by some of the answers. My bad :(
What you want to do should actually work. Keep in mind however that if north is on top, the horizontal (x) axis is the LONGITUDE and the vertical (y) axis is the LATITUDE (I think you might have confused this).
If you parametrize the line as lat = func(long) you will run into trouble with vertical lines (i.e. those going exactly north south) as the latitude varies while the longitude is fixed.
Therefore I'd rather use another parametrization:
long(alpha) = long_1 + alpha * (long_2 - long_1)
lat(alpha) = lat_1 + alpha * (lat_2 - lat_1)
and vary alpha from 0 to 1.
This will not exactly coincide with a great circle (shortest path on a sphere) but the smaller the region you are looking at, the less noticeable the difference will be (as others posters here pointed out).
Here is a distance formula I use that may help. This is using javascript.
function Distance(lat1, lat2, lon1, lon2) {
var R = 6371; // km
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 c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c * 0.621371;
var r = Math.round(d * 100) / 100;
return r;
}
For short distances, where the earth curvature doesn't make a significant difference, it works fine to draw a line using regular two-dimensional geometry.
For longer distances the shortest way between two lines does not project as a straight line on a map, but as a curve. (For example, the shortest way from Sweden to Alaska would be straight over the noth pole, not past Canada and Iceland.) You would have to use three-dimensional geometry to draw a line on a surface of a sphere, then project that onto the map in the same way the earth surface is projected on the map.
Is your goal to find this equation or to actually draw a line?
If the latter, since you're using the Maps API, specify geodesic: true and draw it with a Polyline:
http://code.google.com/apis/maps/documentation/javascript/reference.html#Polyline