I render a map with a route from point A to point B and zoom to the bounding box of the route.
I also render an associated information bubble for point B (destination).
This specific portion of my application does not involve user intervention so it is completely unattended
The map is also printed
Point B is always different
using Enterprise Javascript API v2.5.4
My issue is that many times the info bubble overlays the route polyline which obstructs the route.
I see InfoBubble alignment options for left, right, above, below but these will not help as points are dynamic
Any suggestions on positioning of Info Bubble to assure it does not overlay route?
The defaultXAlignment and defaultYAlignment properties can be used to set a preferred position of the InfoBubble.
You can use the defaultXAlignment and defaultYAlignment properties to alter the preferred offset for the Infobubble e.g.:
var infoBubbles = new nokia.maps.map.component.InfoBubbles();
infoBubbles.options.set("defaultXAlignment",
nokia.maps.map.component.InfoBubbles.ALIGNMENT_RIGHT);
infoBubbles.options.set("defaultYAlignment",
nokia.maps.map.component.InfoBubbles.ALIGNMENT_ABOVE);
map.components.add(infoBubbles);
In your case I would suggest the following.
When you zoom to the bounding box of the route add a padding factor to ensure there is space to display the infoBubble. The simplest way would be to zoom out one more stop, but you could use the padding property of the nokia.maps.map.Display to ensure you have enough space when you zoom(). See
nokia.maps.map.Display.setPadding()
Secondly calculate the bearing between the start and endpoints:
Number.prototype.toRad = function() {
// convert degrees to radians
return this * Math.PI / 180;
}
function bearing(lat1, lon1, lat2, lon2) {
lat1 = lat1.toRad(); lat2 = lat2.toRad();
var dLon = (lon2-lon1).toRad();
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1) * Math.sin(lat2) -
Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
return Math.atan2(y, x);
}
If the result is between 0 and Math.PI/2 you are travelling North-East fix the Infobubble to display on the right and above.
If the result is between Math.PI/2 and Math.PI you are travelling South-East fix the Infobubble to display on the right and below.
If the result is between -Math.PI and -Math.PI/2 you are travelling South-West fix the Infobubble to display on the left and below.
If the result is between -Math.PI/2 and 0 you are travelling North-West fix the Infobubble to display on the left and above.
You can find more information about completely fixing an InfoBubble the answer here
Related
Using the below formula, I can get the bearing between two locations on a globe, but when that angle is used to show the direction between two points on a EPSG:3857 (Mercator) map, the angle is sometimes off by a few degrees, causing the icon to point in the wrong direction.
(Math.atan2(b.y - a.y, b.x - a.x) * 180 / Math.PI)
For example, if I draw a line on my map between two locations and place an icon next to it (direction computed using said function), you can see the issue: the icon should be rotated a few degrees more clockwise. I believe this is caused by the curvature of the earth, because depending on the locations of the points, the angle is off by different amounts.
I was able to resolve this by using the below formula:
function bearing(startLat, startLng, destLat, destLng) {
return ((Math.atan2(0,1) - Math.atan2((destLat - startLat),(destLng - startLng)))*180/Math.PI+180) - 85
}
I apologize for the vague title, I really can't figure out a batter way to sum it up. Suggestions are more than welcome.
I'm working on a project that really doesn't NEED a google map, it'd just create extra overhead for this project. But, I can't figure out how to do this WITHOUT google maps... so far.
If I were to place a graphical overlay of a floor plan into google maps, I could use a browser location to approximate a users position. For this, it's a VERY large place, so there's room for some poor accuracy.
Now what I'm trying to do is knowing the bounding of that overlay, place the image into a div, then calculate the users position in the div based on their browser lat/lng.
I'm able to get close using an image that is square in that the top of the sides are horizontal and vertical, because that crates my area. But since none of the world works like that, I need an area that isn't square to appear squared. I'm really struggling on the math.
Here's what I'm trying to do!
And here's a link that tests my concept, but doesn't account for an image that needs to be rotated on a map: http://www.freeptools.com/mapster/singlerun/maptest.php
Like I said, I'm sure that this can be done, but I haven't been able to figure out the math for it just yet. It's driving me crazy.
Here's my code that's doing the magic. A function gets the browser coordinates and sends them to the initialize function. Then based on mapping it myself earlier, I have the bounds of the image I'm trying to map. I'm using the haversine formuls to try to get the height and width at the points of the user (since the map curves, it'll be inaccurate anywhere else) position, then the distance from the top and left most points, then taking the distance from the left/top and dividing it by the width/height to get a percentage of how far they are from the left/top to position the dot. This idea, while I can't get it to be super accurate, works in this example, because the image lines up horizontally on a map. My problem is how do I calculate those distances when the image is rotated on a map? I can't figure out that math.
function initialize(y3, x3) { //lat, lon
//var overlayBounds = new google.maps.LatLngBounds(new google.maps.LatLng(41.11246878918085, -90.5712890625), new google.maps.LatLng(47.68652571374621, -82.001953125));
var box = $("#map_canvas");
//image bounds
var x1 = -82.001953125;
var x2 = -90.5712890625;
var y1 = 41.11246878918085;
var y2 = 47.68652571374621;
var boxWidth = x2 - x1;
var boxHeight = y2 - y1;
//now we need to figure out where this rests, first we get the percentage
//var posLeft = haversine(y3, x1, y3, x3); //(x3 - x1);
var posLeft = (x3 - x1);
var posLeftPct = (posLeft/boxWidth)*100;
//var posTop = haversine(y2, x3, y3, x3); //(y2 - y3);
var posTop = (y2 - y3);
var posTopPct = (posTop/boxHeight)*100;
box.append('<img src="http://www.freeptools.com/mapster/icons/icon23.png" style="position:absolute; z-index:200; right:'+posLeftPct+'%; top:'+posTopPct+'%">');
}
Assuming the area is a parallelogram, you'll need to know 3 of the vertices of the area and the width/height of the area where you want to draw the pin(e.g. the floorplan-image).
The following will use the Geo-and LatLon-libraries from http://www.movable-type.co.uk/scripts/latlong.html
An image for better understanding:
initially calculate some values:
bearings(nw to sw and nw to ne) via LatLon.bearingTo
distances(sw to nw and ne to nw) via LatLon.distanceTo
now calculate the intersections(marked as intersection-x and intersection-y in the image) via LatLon.intersection
The calculation would be:
intersection-x:
LatLon.intersection(ne, bearingNWtoSW, target, bearingNWtoNE)
intersection-y:
LatLon.intersection(sw, bearingNWtoNE, target,bearingNWtoSW)
now calculate percentual values for the distance of border-north to target and border-west to target:
border-north to target:
((distanceNWtoNE-(target.distanceTo(intersection-x)))*100)/distanceNWtoNE
border-west to target:
((distanceNWtoSW-(target.distanceTo(intersection-y)))*100)/distanceNWtoSW
Finally calculate the coordinates based on the width/height of the given floorplan and the results of the previous calculation
x=((width*distanceBorderNorthToTargetInPercent)/100);
y=((height*distanceBorderWestToTargetInPercent)/100);
A method(extension of the mentioned LatLon-library) that performs all these calculations:
/**
*#param w int width of the drawing
*#param h int height of the drawing
*#param sw object LatLon of southwest of the drawing
*#param nw object LatLon of northwest of the drawing
*#param ne object LatLon of northeast of the drawing
*#return mixed object with x/y-coordinates or null when LatLon is outside of the area
**/
LatLon.prototype.translate = function(w,h,sw,nw,ne) {
var x = {distance:nw.distanceTo(ne),bearing:nw.bearingTo(ne)},
y = {distance:nw.distanceTo(sw),bearing:nw.bearingTo(sw)},
intersectionY = LatLon.intersection(sw, x.bearing, this, y.bearing),
intersectionX = LatLon.intersection(ne, y.bearing, this, x.bearing),
distanceX,distanceY;
if(intersectionX && intersectionY){
distanceX=((x.distance-(this.distanceTo(intersectionX)))*100)/x.distance,
distanceY=((y.distance-(this.distanceTo(intersectionY)))*100)/y.distance;
return {x:((w*distanceX)/100),y:((h*distanceY)/100)};
}
return null;
};
Demo:http://jsfiddle.net/doktormolle/nsbqpcvg/embedded/result/
Hover the highlighted floorplan on the google-map to see the result of the calculation(the google-Maps-API is only used for the demonstration, the calculation will be done without using the Maps-API)
I'm trying to draw a grid of rectangles on top of the map tiles using the Javascript API and highlight (switch fillColor for) whichever rectangle is currently under the mouse pointer. I would expect such a small change to be effective almost immediately.
However the speed at which changes take place is unbearable for something like this, as changes seem to trigger with a delay of maybe 100ms or so. This applies even if I save a reference to one of the rectangles on a 2x2 grid and then change its color from the console. So this seems unlikely (but still possible) to be a performance issue but rather feels like the Maps simply won't refresh often enough.
Is there maybe a way for me to tell the Maps to redraw a region immediately, or should I use some other way of drawing which would be more performant? I currently have a workaround of using a floating div as the highlight, but it feels a bit wrong and comes with other issues to hack around.
rect = new nokia.maps.map.Rectangle(boundingBox, opts)
...
// slow, but not a deal breaker
map.objects.add(rect)
...
// too slow to happen on every mouseenter/mouseleave event
rect.set('fillColor', '000000')
I'm using the 2.5 version of the Javascript API and I'm targeting mostly Chrome.
You could try map.update(-1, true); to force a redraw the map.
Alternatively, one possible performance improvement (which has several caveats) would be to use an overlay for the grid and only one rectangle. This could be of use if you are trying to highlight the current square region of a map tile as served from the TMS server.
You could add a 256x256 grid (or 128x128 or 64x64 etc) using the code in the question here, and then merely move one rectangle over the map to show the current highlight:
For a given zoom and coordinate, the current tile CoordinateZoomToXY is:
var longitude = coord.longitude,
latitude = coord.latitude,
tilesPerRow = Math.pow(2, zoom),
column,
row;
longitude /= 360;
longitude += 0.5;
latitude = 0.5 - ((Math.log(Math.tan((Math.PI / 4) + (latitude * Math.PI / 360))) / Math.PI) / 2.0);
column = Math.floor(longitude * tilesPerRow);
row = Math.floor(latitude * tilesPerRow);
hence the reverse operation (XYZtoCoordinate) is:
var tilesPerRow = Math.pow(2, zoom),
longitude = column / tilesPerRow * 360.0 - 180.0,
lat_rad = Math.atan(sinh(Math.PI * (1 - 2 * row / tilesPerRow))),
latitude = lat_rad * 180.0 / Math.PI;
and the current tile is:
nokia.maps.geo.BoundingBox.coverAll([
XYZtoCoordinate(zoom, column , row),
XYZtoCoordinate(zoom, column + 1, row + 1)]));
If you added this to the listener and just moved one rectangle, it may help as you would only need to update one map object each time.
I've got a custom map type API I need to talk to.
I'd like to get the map images by using the 'tile' function of Google Maps. I've got the tiles setup like this: https://developers.google.com/maps/documentation/javascript/examples/maptype-overlay?hl=nl
The API to get the different tileimages requires me to have a center point for each of the tiles.
So how do I get/calculate the center LatLng point for each tile?
Ok I figured it out.
Here's what I did.
You have a tile coordinate for each tile (x,y) and zoom and a tile size.
So first we make a new Google MapsPoint to the center point of the tile:
var centerpoint = new google.maps.Point((coord.x * tileSize) + (tileSize / 2), ((coord.y + 1) * tileSize) + (tileSize / 2));
Then convert those x,y coordinates to LatLng (conversion functions and the math behind it can be found on http://www.maptiler.org)
So it was actually pretty simple...
You can calculate it also directly:
n = 2 ^ zoom
lon_deg = xtile / n * 360.0 - 180.0
lat_rad = arctan(sinh(π * (1 - 2 * ytile / n)))
lat_deg = lat_rad * 180.0 / π
Source and code samples in various languages: http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_numbers_to_lon..2Flat.
I'm drawing points on a map with OpenLayers like in this example: http://dev.openlayers.org/examples/draw-feature.html
Now I want to know, which area (in meters) is covered by such a drawn point. I know, this depends on the zoom level. And this is also my plan: I want to draw my points with a different size - depending on the zoom level. If the zoom level is maximum, the point should be big. If the zoom level is low, the point should be drawn very small.
Has anyone an idea, how to calculate the point size from pixel to meter?
You could use the map's resolution, which is defined as map units per pixel.
So, assuming your map units are meters, the required pixel size would be:
size_in_meters / map_resolution.
You can use the above calculation in a style map to have points' sizes change dynamically as map resolution changes:
styleMap = new OpenLayers.StyleMap({
'default': new OpenLayers.Style({
pointRadius: "${getSize}"
},
{ context: {
getSize: function(feature) {
return size_in_meters / feature.layer.map.getResolution();
}}
})
});
Maybe you can use a regular polygon (1) instead of the point, with its radius depending on zoom levels.
Then you can call getArea(2) on the obtained geometry.
If you map projection unit is meter, you get it.
1 - http://www.openlayers.org/dev/examples/regular-polygons.html
2 - http://dev.openlayers.org/docs/files/OpenLayers/Geometry-js.html#OpenLayers.Geometry.getArea
HTH,
you can use that constant object to manage your conversions:
ol.proj.METERS_PER_UNIT
/**
* Meters per unit lookup table.
* #const
* #type {Object.<ol.proj.Units, number>}
* #api stable
*/
ol.proj.METERS_PER_UNIT = {};
ol.proj.METERS_PER_UNIT[ol.proj.Units.DEGREES] =
2 * Math.PI * ol.sphere.NORMAL.radius / 360;
ol.proj.METERS_PER_UNIT[ol.proj.Units.FEET] = 0.3048;
ol.proj.METERS_PER_UNIT[ol.proj.Units.METERS] = 1;
And read about it here:
http://openlayers.org/en/v3.1.1/apidoc/proj.js.html