checking whether part of google map is land or water [duplicate] - javascript

..and then Google-maps "divide the waters from the waters"
Well, not in the biblical sense but..
I would like to know what options I have in order to verify if a point of [Lat, Lon] is Land or Water.
Google Maps obviously has this data (the bodies of water are blue) - but is there something in the API that I can use for that? And if not - are they not serving it because they never thought of it? Or because it is too complicated?
I have not found any info on the matter - except some similar questions here (like finding type of terrain, or elevation - but it is not exactly what I need).
Is there separated layer for that? An option? Command? Or should I go to do that manually?
The only way that I can think of how to approach this (should I need to do that manually) is to check every served tile for the exact point - and then check RGB value for that Google map hue.
This is only on theory - because in practice - I have no idea how to accomplish that, the first obstacle being that I do not know how I can convert a pixel location on a tile to [LatLon] point for example
A ready made solution would be much easier.
Note that I do not need ALL the water in the world (for example - I do not care about streams, small ponds, most rivers or your neighbor's swimming pool. I need the points where a person can venture without the aid of a floating vehicle)
EDIT I
After reading comments:
The elevation method is not reliable, there are too many places BELOW sea-level (you can see a list of the "deepest" 10 here http://geology.com/below-sea-level/ ) and there are too many land-locked water bodies ABOVE sea level (lakes).
The reverse geolocation method is not reliable because it will return a Geo-political entity, like city, or state - or ZERO many times.
I have already looked into those pseudo-solutions before asking the question - but none of them actually answered the question - those methods are bad "guessing" at best.

These are 2 different ways, you may try:
You can use Google Maps Reverse Geocoding . In result set you can determine whether it is water by checking types. In waters case the type is natural_feature. See more at this link http://code.google.com/apis/maps/documentation/geocoding/#Types.
Also you need to check the names of features, if they contain Sea, Lake, Ocean and some other words related to waters for more accuracy. For example the deserts also are natural_features.
Pros - All detection process will be done on client's machine. No need of creating own server side service.
Cons - Very inaccurate and the chances you will get "none" at waters is very high.
You can detect waters/lands by pixels, by using Google Static Maps. But for this purpose you need to create http service.
These are steps your service must perform:
Receive latitude,longitude and current zoom from client.
Send http://maps.googleapis.com/maps/api/staticmap?center={latitude,longitude}&zoom={current zoom`}&size=1x1&maptype=roadmap&sensor=false request to Google Static Map service.
Detect pixel's color of 1x1 static image.
Respond an information about detection.
You can't detect pixel's color in client side. Yes , you can load static image on client's machine and draw image on canvas element. But you can't use getImageData of canvas's context for getting pixel's color. This is restricted by cross domain policy.
Prons - Highly accurate detection
Cons - Use of own server resources for detection

It doesn't seem possible with any current Google service.
But there are other services, like Koordinates Vector JSON Query service! You simply query the data in the URL, and you get back a JSON/XML response.
Example request: http://api.koordinates.com/api/vectorQuery.json?key=YOUR_GEODATA_KEY&layer=1298&x=-159.9609375&y=13.239945499286312&max_results=3&radius=10000&geometry=true&with_field_names=true
You have to register and supply your key and selected layer number. You can search all their repository of available layers. Most of the layers are only regional, but you can find global also, like the World Coastline:
When you select a layer, you click on the "Services" tab, you get the example request URL. I believe you just need to register and that's it!
And now the best:
You can upload your layer!
It is not available right away, hey have to process it somehow, but it should work! The layer repository actually looks like people uploaded them as they needed.

There is a free web API that solves exactly this problem called onwater.io. It isn't something built into Google maps, but given a latitude and longitude it will accurately return true or false via a get request.
Example on water:
https://api.onwater.io/api/v1/results/23.92323,-66.3
{
lat: 23.92323,
lon: -66.3,
water: true
}
Example on land:
https://api.onwater.io/api/v1/results/42.35,-71.1
{
lat: 42.35,
lon: -71.1,
water: false
}
Full disclosure I work at Dockwa.com, the company behind onwater. We built onwater to solve this problem ourselves and help the community. It is free to use (paid for high volume) and we wanted to share :)

I thought it was more interesting to do this query locally, so I can be more self-reliant: let's say I want to generate 25000 random land coordinates at once, I would rather want to avoid calls to possibly costly external APIs. Here is my shot at this in python, using the python example mentionned by TomSchober. Basically it looks up the coordinates on a pre-made 350MB file containing all land coordinates, and if the coordinates exist in there, it prints them.
import ogr
from IPython import embed
import sys
drv = ogr.GetDriverByName('ESRI Shapefile') #We will load a shape file
ds_in = drv.Open("land_polygons.shp") #Get the contents of the shape file
lyr_in = ds_in.GetLayer(0) #Get the shape file's first layer
#Put the title of the field you are interested in here
idx_reg = lyr_in.GetLayerDefn().GetFieldIndex("P_Loc_Nm")
#If the latitude/longitude we're going to use is not in the projection
#of the shapefile, then we will get erroneous results.
#The following assumes that the latitude longitude is in WGS84
#This is identified by the number "4236", as in "EPSG:4326"
#We will create a transformation between this and the shapefile's
#project, whatever it may be
geo_ref = lyr_in.GetSpatialRef()
point_ref=ogr.osr.SpatialReference()
point_ref.ImportFromEPSG(4326)
ctran=ogr.osr.CoordinateTransformation(point_ref,geo_ref)
def check(lon, lat):
#Transform incoming longitude/latitude to the shapefile's projection
[lon,lat,z]=ctran.TransformPoint(lon,lat)
#Create a point
pt = ogr.Geometry(ogr.wkbPoint)
pt.SetPoint_2D(0, lon, lat)
#Set up a spatial filter such that the only features we see when we
#loop through "lyr_in" are those which overlap the point defined above
lyr_in.SetSpatialFilter(pt)
#Loop through the overlapped features and display the field of interest
for feat_in in lyr_in:
# success!
print lon, lat
check(-95,47)
I tried a dozen coordinates, it works wonderfully. The "land_polygons.shp" file can be downloaded here, compliments of OpenStreetMaps. (I used the first WGS84 download link myself, maybe the second works as well)

This what I use and it is working not too bad... you can improve the test if you have more cpu to waste by adding pixels.
function isItWatter($lat,$lng) {
$GMAPStaticUrl = "https://maps.googleapis.com/maps/api/staticmap?center=".$lat.",".$lng."&size=40x40&maptype=roadmap&sensor=false&zoom=12&key=YOURAPIKEY";
//echo $GMAPStaticUrl;
$chuid = curl_init();
curl_setopt($chuid, CURLOPT_URL, $GMAPStaticUrl);
curl_setopt($chuid, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($chuid, CURLOPT_SSL_VERIFYPEER, FALSE);
$data = trim(curl_exec($chuid));
curl_close($chuid);
$image = imagecreatefromstring($data);
// this is for debug to print the image
ob_start();
imagepng($image);
$contents = ob_get_contents();
ob_end_clean();
echo "<img src='data:image/png;base64,".base64_encode($contents)."' />";
// here is the test : I only test 3 pixels ( enough to avoid rivers ... )
$hexaColor = imagecolorat($image,0,0);
$color_tran = imagecolorsforindex($image, $hexaColor);
$hexaColor2 = imagecolorat($image,0,1);
$color_tran2 = imagecolorsforindex($image, $hexaColor2);
$hexaColor3 = imagecolorat($image,0,2);
$color_tran3 = imagecolorsforindex($image, $hexaColor3);
$red = $color_tran['red'] + $color_tran2['red'] + $color_tran3['red'];
$green = $color_tran['green'] + $color_tran2['green'] + $color_tran3['green'];
$blue = $color_tran['blue'] + $color_tran2['blue'] + $color_tran3['blue'];
imagedestroy($image);
var_dump($red,$green,$blue);
//int(492) int(570) int(660)
if($red == 492 && $green == 570 && $blue == 660)
return 1;
else
return 0;
}

Checkout this article. It accurately detects if something is on the water without needing a server. It's a hack that relies on the custom styling feature in Google Maps.

In addition to the reverse geocoding -- as Dr Molle has pointed out, it may return ZERO_RESULTS -- you could use the Elevation service. If you get zero results by reverse geocoding, get the elevation of the location. Generally, the sea gets a negative number as the seabed is below sea level. There's a fully-worked example of the elevation service.
Bear in mind that as Google don't make this information available any other method is just a guess and guesses are inherently inaccurate. However using the type returned by reverse geocoding, or the elevation if type is not available, will cover most eventualities.

This method is totally unreliable.
In fact, the returned data will totally depend on what part of the world you are working with.
For example, I am working in France.
If I click on the sea on the coast of France, Google will return the nearest LAND location it can "guess" at.
When I requested information from Google for this same question, they answered that they are unable to accurately return that the point requested in on a water mass.
Not a very satisfactory answer, I know.
This is quite frustrating, especially for those of us who provide the user with the ability to click on the map to define a marker position.

If all else fails you could always try checking the elevation at the point and for some distance about - not many things other than water tend to be completely flat.

Unfortunately this answer isn't within the Google Maps API and the referenced resource is not free, but there's a web service provided by DynamicGeometry that exposes an operation GetWaterOrLand which accepts a latitude/longitude pair (you can see a demo here).
My understanding of how this is implemented is by using water body shape files. How exactly these shape files are used with the Google Maps API, but you might be able to get some insight from the linked demo.
Hope that helps in some way.

Here's another example in pure JavaScript: http://jsfiddle.net/eUwMf/
As you can see, the ideia is basically the same as rebe100x, getting the image from Google static map API, and read the first pixel:
$("#xGps, #yGps").change(function() {
var img = document.getElementById('mapImg');
// Bypass the security issue : drawing a canvas from an external URL.
img.crossOrigin='anonymous';
var xGps = $("#xGps").val();
var yGps = $("#yGps").val();
var mapUrl = "http://maps.googleapis.com/maps/api/staticmap?center=" + xGps + "," + yGps +
"&zoom=14&size=20x20&maptype=roadmap&sensor=false";
// mapUrl += "&key=" + key;
$(img).attr("src", mapUrl);
var canvas = $('<canvas/>')[0];
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
var pixelData = canvas.getContext('2d').getImageData(1, 1, 1, 1).data;
if (pixelData[0] == 164 &&
pixelData[1] == 190 &&
pixelData[2] == 220) {
$("#result").html("Water");
} else {
$("#result").html("Not water");
}
});

See the answer I gave to a similar question - it uses "HIT_TEST_TERRAIN" from the Earth Api to achieve the function.
There is a working example of the idea I put together here: http://www.msa.mmu.ac.uk/~fraser/ge/coord/

If List<Address> address returns 0 , you can assume this location as ocean or Natural Resources.Just add Below Code in Your response Method of Google Places API Response.
Initialize Below List as mentioned
List<Address> addresses = geocoder.getFromLocation(latLng.latitude, latLng.longitude, 1);
if (addresses.size()==0)
{
Toast.MakeText(getApplicationContext,"Ocean or Natural Resources selected",Toast.LENGTH_SHORT).show();
}else{
}

I would recommend rolling your own here. You can use tools like GDAL to query the contents under a point in a shapefile. You can get shapefiles for US geography from many sources including the US Census Bureau.
This can be done via GDAL binaries, the source C, or via swig in Java, Python, and more.
Census Maps
GDAL Information
Point Query Example in Python

Here is a simple solution
Because Google does not provide reliable results with regards to coordinates that lay on either ocean or inland bodies of water you need to use another backup service, such as Yandex, to help provide that critical information when it is missing. You most likely would not want to use Yandex as your primary geocoder because Google is far superior in the reliability and completeness of the worlds data, however Yandex can be very useful for the purpose of retrieving data when it relates to coordinates over bodies of water, so use both.
Yandex Documentation: https://api.yandex.com.tr/maps/doc/geocoder/desc/concepts/input_params.xml
The steps to retrieve Ocean name:
1.) Use Google first to reverse geocode the coordinate.
2.) If Google returns zero results, it is 99% likely the coordinate lies over an ocean. Now make a secondary reverse geocoding request with the same coordinates to Yandex. Yandex will return a JSON response with for the exact coordinates, within this response will be two "key":"value" pairs of importance
["GeoObject"]["metaDataProperty"]["GeocoderMetaData"]["kind"]
and
["GeoObject"]["name"]
Check the kind key, if it == "hydro" you know you are over a body of water, and because Google returned zero results it is 99.99% likely this body of water is an ocean. The name of the ocean will be the above "name" key.
Here is an example of how I use this strategy written in Ruby
if result.data["GeoObject"]["metaDataProperty"]["GeocoderMetaData"]["kind"] == "hydro"
ocean = result.data["GeoObject"]["name"]
end
The steps to retrieve an Inland Body of Water name:
For this example assume our coordinate lies in a lake somewhere:
1.) Use Google first to reverse geocode the coordinate.
2.) Google will most likely return a result that is a prominent default address on land nearby. In this result it supplies the coordinates of the address it returned, this coordinate will not match the one you provided. Measure the distance between the coordinate you supplied and the one returned with the result, if it is significantly different (for example 100 yards) then perform a secondary backup request with Yandex and check to see the value of the "kind" key, if it is "hydro" then you know the coordinate lies on water. Because Google returned a result as opposed to the example above, it is 99.99% likely this is an inland body of water so now you can get the name. If "kind" does not == "hydro" then use the Google geocoded object.
["GeoObject"]["metaDataProperty"]["GeocoderMetaData"]["kind"]
and
["GeoObject"]["name"]
Here is the same code written in Ruby to get inland_body_of_water
if result.data["GeoObject"]["metaDataProperty"]["GeocoderMetaData"]["kind"] == "hydro"
inland_body_of_water = result.data["GeoObject"]["name"]
end
A note about Licensing: As far as I know Google does not allow you to use their data to display on any other maps other than those Google offers. Yandex however has very flexible licensing, and you can use their data to be displayed on Google maps.
Also Yandex has a a high rate limit of 50,000 request / day free of charge, and with no required API key.

I managed to get quite close by using the Google Elevation API. Here's an image of the results:
You see the hexagons pretty much stay on land even though a rectangular perimeter is defined that goes partly over water. In this case I did a quick check from Google Maps itself and the minimum elevation on land was about 8-9m so that was my threshold. The code is mostly copy/pasted from Google documentation and Stack Overflow, here's the full gist:
https://gist.github.com/dvas0004/fd541a0502528ebfb825

As a complete novice to Python I couldn't get SylvainB's solution to work with the python script that checks if coordinates are on land. I managed to figure it out however, by downloading OSGeo4W (https://trac.osgeo.org/osgeo4w/) and then installed everything I needed pip, Ipython, and checked that all the imports specified were there. I saved the following code as a .py file.
Code to check if coordinates are on land
###make sure you check these are there and working separately before using the .py file
import ogr
from IPython import embed
from osgeo import osr
import osgeo
import random
#####generate a 1000 random coordinates
ran1= [random.uniform(-180,180) for x in range(1,1001)]
ran2= [random.uniform(-180,180) for x in range(1,1001)]
drv = ogr.GetDriverByName('ESRI Shapefile') #We will load a shape file
ds_in = drv.Open("D:\Downloads\land-polygons-complete-4326\land-polygons-complete-4326\land_polygons.shp") #Get the contents of the shape file
lyr_in = ds_in.GetLayer(0) #Get the shape file's first layer
#Put the title of the field you are interested in here
idx_reg = lyr_in.GetLayerDefn().GetFieldIndex("P_Loc_Nm")
#If the latitude/longitude we're going to use is not in the projection
#of the shapefile, then we will get erroneous results.
#The following assumes that the latitude longitude is in WGS84
#This is identified by the number "4236", as in "EPSG:4326"
#We will create a transformation between this and the shapefile's
#project, whatever it may be
geo_ref = lyr_in.GetSpatialRef()
point_ref=osgeo.osr.SpatialReference()
point_ref.ImportFromEPSG(4326)
ctran=osgeo.osr.CoordinateTransformation(point_ref,geo_ref)
###check if the random coordinates are on land
def check(runs):
lon=ran1[runs]
lat=ran2[runs]
#Transform incoming longitude/latitude to the shapefile's projection
[lon,lat,z]=ctran.TransformPoint(lon,lat)
#Create a point
pt = ogr.Geometry(ogr.wkbPoint)
pt.SetPoint_2D(0, lon, lat)
#Set up a spatial filter such that the only features we see when we
#loop through "lyr_in" are those which overlap the point defined above
lyr_in.SetSpatialFilter(pt)
#Loop through the overlapped features and display the field of interest
for feat_in in lyr_in:
return(lon, lat)
###give it a try
result = [check(x) for x in range(1,11)] ###checks first 10 coordinates
I tried to get it to work in R but I had a nightmare trying to get all the packages you need to install so stuck to python.

Here's a typed async function that returns true or false if a lat/lng is water or not. No need to pay for external api's. You must enable static maps on google cloud though.
async function isLatLngWater(lat: number, lng: number) {
return new Promise<boolean>((resolve) => {
const img = new Image();
img.crossOrigin = "Anonymous";
img.onload = () => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
ctx!.drawImage(img, 0, 0);
const { data } = ctx!.getImageData(10, 10, 1, 1);
if (data[0] == 156 && data[1] == 192 && data[2] == 249) {
canvas.remove();
resolve(true);
} else {
canvas.remove();
resolve(false);
}
};
img.src =
"https://maps.googleapis.com/maps/api/staticmap?center=" +
lat +
"," +
lng +
"&size=40x40&maptype=roadmap&sensor=false&zoom=20&key=" +
import.meta.env.VITE_GM_MSTAT;
});
}

There an API service called IsItWater.com which will let you check:
Request
curl 'https://isitwater-com.p.rapidapi.com/?latitude=41.9029192&longitude=-70.2652276&rapidapi-key=YOUR-X-RAPIDAPI-KEY'
Response
{
"water": true,
"latitude": 41.9029192,
"longitude": -70.2652276
}

I have a different solution here.
In current google map implementation, it does not calculate direction/distance from a water location to land location and vice versa. Why dont we use this logic to determine if the point is land or water.
For example lets take this example
if we want to determine, if a point x is land or water, then
let us check the direction between point x and a known point y which is land. If it determines the direction/distance then point x is land or else it is water.

Related

What is the Ideal way to create a Triangle-Coordinates in Google Map API with two location points consisting of latitude & longitude

I have a scenario in my JavaScript application where I have the coordinates of a starting point which consist of Latitude and Longitude, similarly an ending point with it's respective coordinates.
Now I need to search for a location which basically provides with a set of coordinates and find if the recently entered location lies in between the previously mentioned starting point or ending point. However, the location does not need to match exactly within the points of the path of the start and end point. That is even if the location lies around the distance of say 2-3 km from the derived path, it should give a match.
I believe that we can create a triangle by providing three coordinates i.e start-point, end-point and a third point. So once the triangle is formed we can use google.maps.geometry.poly.containsLocation method to find if our searched location is present inside this triangle.
So my question is how can we get a third point to create a triangle which will provide locations that are nearby within 2-3 km from start to end point.
Else is there any alternate approach to deal with my use case?
Use googlemap's geometry library
This function specifically
isLocationOnEdge
Here's an example
0.001 tolerance value would be 100m
var isLocationNear = google.maps.geometry.poly.isLocationOnEdge(
yourLatLng,
new google.maps.Polyline({
path: [
new google.maps.LatLng(point1Lat, point1Long),
new google.maps.LatLng(point2Lat, point2Long),
]
}),
.00001);
Please note that the following answer assumes Plane Geometry where you should be using Spherical Geometry instead. Although this will be fine for less accurate purposes (like approximate distance, etc..)
It seems more of a geometry question than a programming question. A triangle like you mentioned won't be able to cover a straight line path in a uniform way. The situation can be thought of more like a distance between point and a line problem (Refer the given diagram
Here you can just find the distance between point C and line AB which you can check whether it's below 2.5 KMs (I've omitted all the units and conversions for simplicity)
Please note that you will also need to convert the distances from radian to appropriate units that you require using haversine formula, etc. which is not a trivial task (https://www.movable-type.co.uk/scripts/latlong.html).

Calculate GPS coordinates from a starting point

Is there a library (JS or .NET) or a set of formulas that would help me calculate set of gps coordinates based on starting location, direction and distance.
For example.
I have an exact lon/lat of a certain location. I'd like to calculate an exact lon/lot 5 miles NE from that location.
With Javascript you can use the geodesy library (under the MIT license), which is the code behind the Movable-Type.co.uk website.
For example:
const LatLon = require('geodesy').LatLonSpherical;
const p1 = new LatLon(59.912140, 10.743796);
const distance = 42*10**3;
const bearing = 240.0
const p2 = p1.rhumbDestinationPoint(distance, bearing);
console.log(p2); // LatLon { lat: 59.72328246275707, lon: 10.093154371545552 }
This finds a second point given initial position, distance and bearing. This particular code is for Node, but it can also be used in the browser.
It should be noted that there are often several different ways to do the various calculations. Some are complex and accurate, while some are simple and less accurate. Using a spherical earth model, as I did in my example, is less accurate, but good enough for ordinary use.
Another library you could check out is geolib.
Perhaps this might or might not fit your needs but have you tried Entity Framework Spatial?

How do I create a leaflet map with thousands of marks that doesn't crash my browser?

I'm using the leaflet package in R to generate a map with a large number of circles on it. The goal is a map I can publish to my website. The problem I'm having is that as I increase the number of circles, the resulting map loads very slowly, I get "unresponsive script" warnings and ultimately it completely freezes up my browser.
I know this sort of thing is possible, because I've found a leaflet map that works the way I want mine to work:
http://cartologic.com/geoapps/map_viewer/5/ny-crimes-2014-dot-density-map
I notice on the above map that the circles don't appear "clickable" like the circles on my map, and that they seem to load in square chunks. I have a hunch that these things are related to my problem. Unfortunately, I'm too much of a novice at leaflet/javascript stuff to figure this out on my own.
Here is a toy example illustrating my problem:
library("leaflet")
library("htmlwidgets")
dots <- data.frame(x=c(runif(10000, -93.701281, -93.533053)),
y=c(runif(10000, 41.515962, 41.644369)))
m <- leaflet(dots) %>%
addTiles('http://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png') %>%
setView(-93.617167, 41.580166, zoom = 12) %>%
addCircles(~x, ~y, weight = 1, radius = 5,
color = "#FFA500", stroke = TRUE, fillOpacity = 0.1)
m
saveWidget(widget = m, file="example.html", selfcontained = TRUE)
mapview can help you here. It builds upon the leaflet library for smaller data sets, but uses special javascript functionality for larger data.
your example with 1 Mio. points:
library(mapview)
library(sp)
dots <- data.frame(x=c(runif(1000000, -93.701281, -93.533053)),
y=c(runif(1000000, 41.515962, 41.644369)))
coordinates(dots) <- ~ x + y
proj4string(dots) <- "+init=epsg:4326"
mapview(dots)
It may still take a while to render, but once rendered it should be quite responsive. Note that mapview is designed to work with spatial* objects, that is why we need the calls to set the coordinate slot and the projection.
For more information have a look here:
http://environmentalinformatics-marburg.github.io/web-presentations/20150723_mapView.html
Hope that helps.
If you want to add a large number of vector objects to a map, it is rare that it can be done easily.
Notice that the raster data is broken into tiles so that all the information does not have to be shown at one time. For your vector data (in this case, circles) you have to do the same thing.
Basically what I like to do is to break the large data set into smaller (vector) tiles, with the same boundaries as the raster tiles you are showing. Duplicate the data if you want it to appear at several zoom level. As you are showing circle, imagine that you partition the circles' center points on the tile boundary.
I have an application similar to this where I basically partition my vector data on tile boundaries and store the information in geojson files. When I get an event that the raster tile has been loaded I can then load the equivalent vector file as a geojson layer (same thing when the raster tile is unloaded). In this way, you can limit the amount of vector data that has to be displayed at any one time.
If you have a lot of points, they are not really going to be visible at low zoom levels anyway, so it might be better just to show them at an appropriate zoom level (perhaps with a different representation at low zooms - like a heat map). This will keep the amount of data being shown at any one time lower.
Since this question has a few upvotes, I'll generally describe both of the solutions I found. Maybe if I have time later I'll get all the files together on GitHub.
First, I found TileMill. Simply load a data file of coordinates into TileMill, style the way you want them to appear, and output tiles (png). Host those tiles on the web somewhere and load them with leaflet. This process was a bit too manual for my liking because TileMill kept crashing when I loaded in csv files that were too large for it to render on my machine.
I found the best solution was use Processing, adapting Robert Manduca's code here: https://github.com/rmanduca/jobmaps. I don't use Python so I rewrote those parts in R and modified the Processing code according to my specifications.
Mapdeck (released on CRAN Aug 2018) uses WebGL (through Deck.gl) and is designed to handle millions of points (depending on your system's hardware of course)
library(mapdeck)
set_token("MAPBOX_TOKEN")
n <- 1e6
dots <- data.frame(x=c(runif(n, -93.701281, -93.533053)),
y=c(runif(n, 41.515962, 41.644369)))
dots$letter <- sample(letters, size = n, replace = T)
mapdeck(
style = mapdeck_style('dark')
) %>%
add_scatterplot(
data = dots
, lon = "x"
, lat = "y"
, fill_colour = "letter"
, radius = 5
, fill_opacity = 50
, layer_id = "dots"
)

Load external data to google maps and build heatmap

In my database I have set of zip codes and number of orders for specific zip code
zip | count
--------------
12-456 | 23
12-100 | 120
12-220 | 93
10-300 | 2
I need a way to show that data as heat map using GoogleMaps.
I found basic example at https://developers.google.com/maps/documentation/javascript/examples/layer-heatmap
It is a nice start, but it requires me to add all the points to my page.
Is there an option to show page and then do request to server to get all the points?
I know that I can make ajax request to server, get json as result and then in loop add those point to taxiData (code from example), but maybe this can be done easier?
In Poland are almost 23 thousands zip codes, so I can have very large dataset from server.
Is it possible to load data as latitude, longitude, count?
I can convert zip codes to locations (GPS coordinates) but I would like to send number of orders in that location.
How can I show heat map for specific region? Like in this sample: http://maps.forum.nu/v3/gm_customTiles.html heat map is only presented for United States.
Any ideas on how to start (links, examples) are big help.
First question:
Well just a thought, you could sample the zip codes and let the user choose the detail. If he wants more detail it will take a little more to load. In low map zooms you don't need that many points. If the user wants more detail in higher map zooms you fetch more. But don't underestimate ajax, I had a web site page that loaded around 9000 database entries and it was ready in around 3 secs, maybe less. Just don't block the page and don't forget to use a loader :)
Second question:
In SQL you could do
SELECT latitude, longitude, COUNT(*) count FROM Table GROUP BY latitude, longitude
This will give you the unique pairs of latitude and longitude and their count. If you want you could play with the decimal points to get more or less accuracy.
Third question:
That map uses MCustomTileLayer. You can see and download the source code here http://code.google.com/p/biodiversity-imageserver/source/browse/trunk/unittest/gmap3/MCustomTileLayer.js?r=49
That site's example:
var hMap = new MCustomTileLayer(map,theme);
var oDiv = document.getElementById('controlsDiv');
var tlcOptions = {
parent: oDiv,
overlay: hMap,
caption: theme.toUpperCase()
}
var tlc = new MTileLayerControl(tlcOptions);
To be honest I prefer a lot more the standard google API, its simpler and prettier. They just produce an image and place it on the map. You can play with the heatmap radius to fill just the zones you want.
Hope it helps :)

Determine timezone from latitude/longitude without using web services like Geonames.org

is there any possibility to determine the timezone of point (lat/lon) without using webservices? Geonames.org is not stable enough for me to use :( I need this to work in PHP.
Thanks
I had this problem a while back and did exactly what adam suggested:
Download the database of cities from geonames.org
convert it to a compact lat/lon -> timezone list
use an R-Tree implementation to efficiently lookup the nearest city (or rather, its timezone) to a given coordinate
IIRC it took less than 1 second to populate the R-Tree, and it could then perform thousands of lookups per second (both on a 5 year old PC).
How exact do your results have to be? If a rough estimate is enough, calculate the offset yourself:
offset = direction * longitude * 24 / 360
where direction is 1 for east, -1 for west, and longitude is in (-180,180)
I ran into this problem while working on another project and looked into it very deeply. I found all of the existing solutions to be lacking in major ways.
Downloading the GeoNames data and using some spatial index to look up the nearest point is definitely an option, and it will yield the correct result a lot of the time, but it can easily fail if a query point is on the wrong side of a time zone border from the nearest point in the database.
A more accurate method is to use a digital map of the time zones and to write code to find the polygon in that map that contains a given query point. Thankfully, there is an excellent map of the time zones of the world available at http://efele.net/maps/tz/world/ (not maintained anymore). To write an efficient query engine, you need to:
Parse the ESRI shapefile format into a useful internal representation.
Write point-in-polygon code to test whether a given query point is in a given polygon.
Write an efficient spatial index on top of the polygon data so that you don't need to check every polygon to find the containing one.
Handle queries that are not contained by any polygon (e.g., in the ocean). In such cases, you should "snap to" the nearest polygon up to a certain distance, and revert to the "natural" time zone (the one determined by longitude alone) in the open ocean. To do this, you will need code to compute the distance between a query point and a line segment of a polygon (this is non-trivial since latitude and longitude are a non-Euclidean coordinate system), and your spatial index will need to be able to return nearby polygons, not just potentially containing polygons.
Each of those are worthy of their own Stack Overflow question/answer page.
After concluding that none of the existing solutions out there met my needs, I wrote my own solution and made it available here:
http://askgeo.com
AskGeo uses a digital map and has a highly optimized spatial index that allows for running more than 10,000 queries per second on my computer in a single thread. And it is thread safe, so even higher throughput is certainly possible. This is a serious piece of code, and it took us a long time to develop, so we are offering it under a commercial license.
It is written in Java, so using it in PHP would involve using:
http://php-java-bridge.sourceforge.net/doc/how_it_works.php
We are also open to porting it for a bounty. For details on the pricing, and for detailed documentation, see http://askgeo.com.
I hope this is useful. It certainly was useful for the project I was working on.
I know this is old, but I spent some time looking for this answer. Found something very useful. Google does time zone lookups by long/lat. No free tier anymore :-/
https://developers.google.com/maps/documentation/timezone/
You should be able to, if you know the polygon of the timezone to see if a given lat/lon is inside it.
World Time Zone Database
Latitude/Longitude Polygon Data
For areas on land, there are some shapefile maps that have been made for the timezones of the tz (Olson) database. They're not updated quite as regularly as the tz database itself, but it's a great starting point and seems to be very accurate for most purposes.
How about this ?
// ben#jp
function get_nearest_timezone($cur_lat, $cur_long, $country_code = '') {
$timezone_ids = ($country_code) ? DateTimeZone::listIdentifiers(DateTimeZone::PER_COUNTRY, $country_code)
: DateTimeZone::listIdentifiers();
if($timezone_ids && is_array($timezone_ids) && isset($timezone_ids[0])) {
$time_zone = '';
$tz_distance = 0;
//only one identifier?
if (count($timezone_ids) == 1) {
$time_zone = $timezone_ids[0];
} else {
foreach($timezone_ids as $timezone_id) {
$timezone = new DateTimeZone($timezone_id);
$location = $timezone->getLocation();
$tz_lat = $location['latitude'];
$tz_long = $location['longitude'];
$theta = $cur_long - $tz_long;
$distance = (sin(deg2rad($cur_lat)) * sin(deg2rad($tz_lat)))
+ (cos(deg2rad($cur_lat)) * cos(deg2rad($tz_lat)) * cos(deg2rad($theta)));
$distance = acos($distance);
$distance = abs(rad2deg($distance));
// echo '<br />'.$timezone_id.' '.$distance;
if (!$time_zone || $tz_distance > $distance) {
$time_zone = $timezone_id;
$tz_distance = $distance;
}
}
}
return $time_zone;
}
return 'none?';
}
//timezone for one NY co-ordinate
echo get_nearest_timezone(40.772222,-74.164581) ;
// more faster and accurate if you can pass the country code
echo get_nearest_timezone(40.772222, -74.164581, 'US') ;
I've written a small Java class to do this. It could be easily translated to PHP. The database is embedded in the code itself. It's accurate to 22km.
https://sites.google.com/a/edval.biz/www/mapping-lat-lng-s-to-timezones
The whole code is basically stuff like this:
if (lng < -139.5) {
if (lat < 68.5) {
if (lng < -140.5) {
return 371;
} else {
return 325;
}
...so I presume a translation to PHP would be easy.
Unfortunately, time zones are not regular enough for some simple function. See the map in Wikipedia - Time Zone
However, some very rough approximation can be calculated: 1 hour difference corresponds to 15 degrees longitude (360 / 24).
Another solution is to import a table of cities with timezones and then to use the Haversine formula to find the nearest city in that table, relevant to your coordinates.
I have posted a full description here: http://sylnsr.blogspot.com/2012/12/find-nearest-location-by-latitude-and.html
For an example of loading the data in MySQL, I have posted an example here (with sources for downloading a small data dump): http://sylnsr.blogspot.com/2012/12/load-timezone-data-by-city-and-country.html
Note that the accuracy of the look-up will be based on how comprehensive your look-up data is.
Credits and References:
MySQL Great Circle Distance (Haversine formula)
You can use time zone boundaries, provided here:
http://www.opensource.apple.com/source/TimeZoneData/
Not sure if this is useful or not, but I built a database of timezone shapes (for North America only), which is painstakingly accurate and current not just for borders, but also for daylight saving time observance. Also shapes for unofficial exceptions. So you could query the set of shapes for a given location could return multiple shapes that apply to that location, and choose the correct one for the time of year.
You can see an image of the shapes at http://OnTimeZone.com/OnTimeZone_shapes.gif. Blue shapes are around areas that do not observe daylight saving time, magenta shapes those that do observe daylight saving time, and neon green shapes (small and tough to see at that zoom level) are for areas with unofficial deviation from the official time zone. Lots more detail on that available at the OnTimeZone.com site.
The data available for download at OnTimeZone.com is free for non-commercial use. The shape data, which is not available for download, is available for commercial license.
I downloaded data that matches 5 digit zip codes to time zones, and data that determines the UTC offset for each time zone during DST and non-DST periods.
Then I simplified the data to only consider the first three digits of the ZIP Code, since all ZIP codes that share the first three digits are very close to each other; the three digits identify a unique mail distribution center.
The resulting Json file does require you to decide whether or not you are subject to DST currently, and it probably has some inaccuracy here and there. But it's a pretty good local solution that is very compact and simple to query.
Here it is:
https://gist.github.com/benjiwheeler/8aced8dac396c2191cf0
I use geocoder.ca
Input any location in North America, Output geocodes, area codes and timezone in json or jsonp.
For example: http://geocoder.ca/1600%20pennsylvania%20avenue,Washington,DC
Area Code: (202)
Time Zone: America/New_York
Json:
{"standard":{"staddress":"Pennsylvania Ave","stnumber":"1600","prov":"DC","city":"WASHINGTON","postal":"20011","confidence":"0.8"},"longt":"-76.972948","TimeZone":"America/New_York","AreaCode":"202","latt":"38.874533"}
You can use Google Timezone api. https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510&timestamp=1331161200&key=YOUR_API_KEY
{
"dstOffset" : 0,
"rawOffset" : -28800,
"status" : "OK",
"timeZoneId" : "America/Los_Angeles",
"timeZoneName" : "Pacific Standard Time"
}
You can get the timezone based on the location in javascript.
function initAutocomplete() {
// Create the autocomplete object, restricting the search to geographical
// location types.
autocomplete = new google.maps.places.Autocomplete(
/** #type {!HTMLInputElement} */(document.getElementById('Location')),
{types: ['geocode']});
// When the user selects an address from the dropdown, populate the address
// fields in the form.
autocomplete.addListener('place_changed', fillInAddress);
}
function fillInAddress() {
// Get the place details from the autocomplete object.
var place = autocomplete.getPlace();
fnGettimezone($('#Location').serialize());
for (var component in componentForm) {
document.getElementById(component).value = '';
document.getElementById(component).disabled = false;
}
// Get each component of the address from the place details
// and fill the corresponding field on the form.
for (var i = 0; i < place.address_components.length; i++) {
var addressType = place.address_components[i].types[0];
if (componentForm[addressType]) {
var val = place.address_components[i][componentForm[addressType]];
document.getElementById(addressType).value = val;
}
}
}
function fnGettimezone(location) {
$.ajax({
url: "http://maps.googleapis.com/maps/api/geocode/json?address=" + location + "&sensor=false",
dataType: 'json',
success: function (result) {
console.log(result);
// result is already a parsed javascript object that you could manipulate directly here
var myObject = JSON.parse(JSON.stringify(result));
lat = myObject.results[0].geometry.location.lat;
lng = myObject.results[0].geometry.location.lng;
console.log(myObject);
$.ajax({
url: "https://maps.googleapis.com/maps/api/timezone/json?location=" + lat + "," + lng + "&timestamp=1331161200&sensor=false",
dataType: 'json',
success: function (result) {
// result is already a parsed javascript object that you could manipulate directly here
var myObject = JSON.parse(JSON.stringify(result));
$('#timezone').val(myObject.timeZoneName);
}
});
}
});
}
This project builds a shapefile from data from OpenStreetMap and link to multiple libraries that handle it.
For PHP you can look at https://github.com/minube/geo-timezone

Categories

Resources