Google maps .fitBounds() and custom offsetCenter? - javascript

I was using this function, which given a latlong, it centers the google maps considering the offsetX and offsetY:
customCenter: function ( latlng, offsetx, offsety ) {
// latlng is the apparent centre-point
// offsetx is the distance you want that point to move to the right, in pixels
// offsety is the distance you want that point to move upwards, in pixels
var self = this;
var scale = Math.pow(2, self.map.getZoom());
google.maps.event.addListenerOnce(self.map,"projection_changed", function() {
var worldCoordinateCenter = self.map.getProjection().fromLatLngToPoint(latlng);
var pixelOffset = new google.maps.Point((offsetx/scale) || 0,(offsety/scale) ||0)
var worldCoordinateNewCenter = new google.maps.Point(
worldCoordinateCenter.x - pixelOffset.x,
worldCoordinateCenter.y + pixelOffset.y
);
var newCenter = self.map.getProjection().fromPointToLatLng(worldCoordinateNewCenter);
self.map.setCenter(newCenter);
});
}
Which I'm using like, so:
self.customCenter (marker.position, $('aside').width(), 0 );
But now they'are asking me to fitbounds with the offset ( so none of the Markers are out of the view
(usually used like so:)
var bounds = new google.maps.LatLngBounds();
bounds.extend(myPlace);
bounds.extend(Item_1);
bounds.extend(Item_2);
bounds.extend(Item_3);
self.map.fitBounds(bounds);
But I don't see a way to combine this two functionalities
( this is because I have an aside floating ( pos: absolute ) over the google maps, just like this image I found online:
)
Any ideas?
-I'm trying like this now:
customCenterWithBounds: function ( offsetx, offsety ) {
// offsetx is the distance you want that point to move to the right, in pixels
// offsety is the distance you want that point to move upwards, in pixels
var self = this;
self.map.fitBounds(self.bounds);
var mapCenter = self.map.getCenter();
var latlng = new google.maps.LatLng (mapCenter.lat(), mapCenter.lng() );
var scale = Math.pow(2, self.map.getZoom());
google.maps.event.addListenerOnce(self.map,"projection_changed", function() {
var worldCoordinateCenter = self.map.getProjection().fromLatLngToPoint(latlng);
var pixelOffset = new google.maps.Point((offsetx/scale) || 0,(offsety/scale) ||0)
var worldCoordinateNewCenter = new google.maps.Point(
worldCoordinateCenter.x - pixelOffset.x,
worldCoordinateCenter.y + pixelOffset.y
);
var newCenter = self.map.getProjection().fromPointToLatLng(worldCoordinateNewCenter);
self.map.setCenter(newCenter);
});
}
But the map would be over-zoomed.. and not even in the center of the visible area..

I ended up doing this
customCenter: function (offsetx, offsety, bounds_obj ) {
if ($(window).width() <= 1200 ) {
offsetx = 0;
}
var self = this;
google.maps.event.addListenerOnce(self.map,"projection_changed", function() {
var latlng;
var fbObj = self.map.fitBounds(self.bounds);
var mapCenter = self.map.getCenter();
latlng = new google.maps.LatLng(mapCenter.lat(), mapCenter.lng());
var scale = Math.pow(2, self.map.getZoom());
var worldCoordinateCenter = self.map.getProjection().fromLatLngToPoint(latlng);
var pixelOffset = new google.maps.Point((offsetx/scale) || 0,(offsety/scale) ||0)
var worldCoordinateNewCenter = new google.maps.Point(
worldCoordinateCenter.x - pixelOffset.x,
worldCoordinateCenter.y + pixelOffset.y
);
var newCenter = self.map.getProjection().fromPointToLatLng(worldCoordinateNewCenter);
self.map.setCenter(newCenter);
});
}

Related

Openlayers 4: Getting Pixeldata from Map appears to have an offset

I have written a function to retrieve pixel data from an openlayers map:
var imagery = new ol.layer.Tile({
source: new ol.source.OSM(),
crossOrigin: 'anonymous'
});
var context;
/**
* Apply a filter on "postcompose" events.
*/
imagery.on('postcompose', function(event) {
context = event.context;
});
function getPixel( latitude, longitude ){
let lat = parseFloat( latitude );
let lon = parseFloat( longitude );
var coord = ol.proj.transform( [lon, lat], 'EPSG:4326', 'EPSG:3857' );
var pixel = map.getPixelFromCoordinate( coord );
if( context == null )
return;
return context.getImageData(pixel[0], pixel[1], 1, 1).data;
}
var map = new ol.Map({
target: 'map',
layers: [imagery],
...
});
The code is based on this link. Also, the transformation from LatLon data to pixel coordinates is used quite extensively and does not provide any problems.
However, the pixel data that is retrieved is somehow offset from the coordinates, so that I see water where there is land and otherwise. As far as I can tell, the offset is a few hundred pixels (or a few meters in lonlat) in x and y direction, and probably the cause lies in the fact that the pixels from the map do not correspond to that of the canvas.
Does anyone have a clue on how to tackle this problem?
Mike's response indeed put me on the right track!
I have included the modified code for those who may run into the same issue:
var pixelRatio;
var context;
var imagery = new ol.layer.Tile({
source: new ol.source.OSM(),
crossOrigin: 'anonymous'
});
/**
* before rendering the layer, determine the pixel ratio
* and save the context
*/
imagery.on('precompose', function(event) {
context = event.context;
pixelRatio = event.frameState.pixelRatio;
context.save();
});
/**
* Restore the context
*/
imagery.on('postcompose', function(event) {
context = event.context;
context.restore();
});
/**
* Get the pixel's rgba data
*/
function getPixel( latitude, longitude, offx, offy ){
let lat = parseFloat( latitude );
let lon = parseFloat( longitude );
var coord = ol.proj.transform( [lon, lat], 'EPSG:4326', 'EPSG:3857' );
var pixel = map.getPixelFromCoordinate( coord );
if( context == null )
return;
var pixelAtClick = context.getImageData(pixel[0]*pixelRatio, pixel[1]*pixelRatio, 1, 1).data;
rgb = [0,0,0,0];
for( var i=0;i<pixelAtClick.length;i++ ){
rgb[i] = pixelAtClick[i];
}
return rgb;
}

Leaflet maxBounds bottom not working

At the moment I make website for a company who wants a custom map on their page. This map is a big a vector map drawn by a graphic designer. So I use leaflet for this but I have an issue. I make it full screen. The issue is I set the bounds and everything and it works on all side except the bottom so when i start moving up and it not goes back to the bottom. The funny thing if I resize the window so for example I make it small and after back to full window size the bottom part works perfectly.
Here is my code so far:
var winh = $(window).height();
var winw = $(window).width();
var maph = $('#map').height();
var mapw = $('#map').width();
var offset = $('#map').offset();
var centerX = offset.left + mapw / 2;
var centerY = offset.top + maph / 2;
var changem = false;
var cbut = $('#building');
var southWest = new L.LatLng(winh, 0);
var northEast = new L.LatLng(0, winw);
var bounds = L.latLngBounds(southWest, northEast);
var map = L.map('map', {
maxBounds: bounds,
maxZoom: 2,
minZoom: 0,
crs: L.CRS.Simple
}).setView([winh, winw], 0);
// .setView([winh, winw], 0)
map.setMaxBounds(bounds);
// map.panTo(new L.LatLng(centerY,centerX));
// ----------------------------------
// Load the images for the map
// ----------------------------------
var imageUrl = 'http://rigailoveyou.exflairdigital.com/public/img/houses.png'
var imageUrl2 = 'http://rigailoveyou.exflairdigital.com/public/img/road.png'
var imageBounds = [[winh,0], [0,winw]];
var map1 = L.imageOverlay(imageUrl, bounds,'Riga I Love You').addTo(map);
var map2 = L.imageOverlay(imageUrl2, bounds).addTo(map);

Orient plane based on vectors

I am want to draw a plane that has an an array of points lying on it (including the origin). The three.js library draws the plane on the origin, facing the xy plane. Right now, I am having trouble of moving the it from the origin to a position such that it contains the points.
So far, I have managed to find a way to orient some planes that lie on the y-axis:
var directionalVectors = __getDirectionVectors(points);
var normal = __getNormalOfPlane(directionalVectors);
var angleXY = __getAngleBetweenPlanes( normal, new THREE.Vector3(0, 0, 1) );
plane.rotateY(- angleXY );
plane.translateY( planeDimensions.width /2.0);
plane.translateX( planeDimensions.height /2.0);
This is how I calculate the direction vectors:
var __getDirectionVectors = function( points ){
var numOfPoints = points.length, i;
var pointOne, pointTwo, directionalVectors = [], directionalVector;
for( i = 0; i < numOfPoints - 1; i++){
pointOne = points[i];
pointTwo = points[i + 1];
directionalVector = new THREE.Vector3().subVectors(pointOne, pointTwo);
directionalVectors.push(directionalVector);
}
return directionalVectors;
};
This is how I calculate the normal:
var __getNormalOfPlane = function(vectors){
var numOfVectors = vectors.length;
var vectorOne, vectorTwo, normal;
if( numOfVectors >= 2){
vectorOne = vectors[0];
vectorTwo = vectors[1];
normal = new THREE.Vector3().crossVectors(vectorOne, vectorTwo);
}
return normal;
};
This is how I calculate the angle between the plane and the XY plane:
//http://www.netcomuk.co.uk/~jenolive/vect14.html
var __getAngleBetweenPlanes = function( normalOne, normalTwo){
var dotPdt = normalOne.dot(normalTwo);
var angle = Math.acos( dotPdt / ( normalOne.length() * normalTwo.length() ) );
return angle;
}
Is there any way I could orient the plane properly for all types of planes?

leaflet: label under polyline

I want to do static labels (under my line and parallel my line) for polyline in leaflet.
For example:
http://jsfiddle.net/jypp24oq/5/
firstpolyline.bindLabel('Even polylines can have labels.', { noHide: true });
But 'noHide' parametr not work for polylines? and also I want to do label parallel my line.
How I can do it?
You wont be able to just bind a label to a polyline. It will need additional coordinates based on the line you will want to bind it to.
Checkout the example on fiddle that includes a helper function called "bindLabelEx" that handles the polyline labeling for you:
https://jsfiddle.net/jZv7W/158/
$(document).ready(function() {
L.Polyline.include({
bindLabelEx: function (content, options) {
if (!this.label || this.label.options !== options) {
this.label = new L.Label(options, this);
}
var latlngs = this.getLatLngs();
var nPoint = latlngs.length;
var lats = [];
var lngs = [];
for(var i = 0; i < nPoint; i++) {
lats.push(latlngs[i].lat);
lngs.push(latlngs[i].lng);
}
var minLat = Math.min.apply(null, lats);
var maxLat = Math.max.apply(null, lats);
var minLng = Math.min.apply(null, lngs);
var maxLng = Math.max.apply(null, lngs);
var pointM = {
lat: (minLat + maxLat) / 2,
lng: (minLng + maxLng) / 2
};
this.label.setContent(content);
this._showLabelAdded = true;
this._showLabel({
latlng: pointM
});
}
});
L.RotatedMarker = L.Marker.extend({
_setPos: function(pos) {
L.Marker.prototype._setPos.call(this, pos);
if (L.DomUtil.TRANSFORM) {
// use the CSS transform rule if available
this._icon.style[L.DomUtil.TRANSFORM] += ' rotate(' + this.options.angle + 'deg)';
} else if (L.Browser.ie) {
// fallback for IE6, IE7, IE8
var rad = this.options.angle * L.LatLng.DEG_TO_RAD,
costheta = Math.cos(rad),
sintheta = Math.sin(rad);
this._icon.style.filter += ' progid:DXImageTransform.Microsoft.Matrix(sizingMethod=\'auto expand\', M11=' +
costheta + ', M12=' + (-sintheta) + ', M21=' + sintheta + ', M22=' + costheta + ')';
}
}
});
L.rotatedMarker = function(pos, options) {
return new L.RotatedMarker(pos, options);
};
//example user location
var userLocation = new L.LatLng(28.735, 77.524);
var map = L.map('map').setView(userLocation, 10);
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © CloudMade'
}).addTo(map);
var pointA = new L.LatLng(28.635308, 77.22496);
var pointB = new L.LatLng(28.984461, 77.70641);
//var pointC = new L.LatLng(29.03, 77.20);
//var pointD = new L.LatLng(28.52, 77.45);
var pointM = new L.LatLng( (pointA.lat + pointB.lat) / 2, (pointA.lng + pointB.lng) / 2);
var pointList = [pointA, pointB];
//var pointList = [pointA, pointB, pointC, pointD];
var firstpolyline = new L.Polyline(pointList, {
color: 'red',
weight: 3,
opacity: 0.5,
smoothFactor: 1
});
firstpolyline.addTo(map).bindLabelEx('Even polylines can have labels.', { noHide: true, showLabelAdded: true });
var angle = Math.atan( (pointB.lat - pointA.lat) / (pointB.lng - pointA.lng) );
angle *= 180 / Math.PI + 5;
var marker = L.rotatedMarker(pointM, {
icon: L.divIcon({
className: 'label',
html: 'Do you want me to do?',
iconSize: [160, 40]
}),
angle: -angle
//draggable: true
});
marker.addTo(map);
/*
var ll = marker.getLatLng();
marker.options.angle = -45 * (180 / Math.PI);
market.setLatLng(ll);
*/
});
You can create labels attached to iconless markers located on the middle point of your polylines by calculating and setting each middle point position with help of following formula,
Math.round(arrPolylinePoints.length / 2) - 1;

Pan to "usable part" of Google Map

I'm using the Google Maps API to embed a map in a web page. The map fills the entire screen, but there's ~400px worth of content on the left side of the screen that mostly covers the map. When I want to pan the map, that area to the left should be treated as though it isn't visible.
I came up with the following code to calculate the "usable part" of the map, which I'd like to be 50px in from the map's edge, and should also avoid the 400px area to the left side of the map:
panIfNotClose = function(latLngs){
if(latLngs.length === 0)
return;
// Get some important values that may not be available yet
// http://stackoverflow.com/a/2833015/802335
var bounds = map.getBounds();
if(!bounds)
return setTimeout(panIfNotClose.bind(this, latLngs), 10);
var overlay = new google.maps.OverlayView();
overlay.draw = function(){};
overlay.setMap(map);
var proj = overlay.getProjection();
if(!proj)
return setTimeout(panIfNotClose.bind(this, latLngs), 10);
// Calculate the "usable part" of the map
var center = bounds.getCenter();
var northEast = bounds.getNorthEast();
var southWest = bounds.getSouthWest();
var northEastPx = proj.fromLatLngToContainerPixel(northEast);
var southWestPx = proj.fromLatLngToContainerPixel(southWest);
var menuPadding = 400;
var newNorthEastPx = new google.maps.Point(northEastPx.x + 50, northEastPx.y + 50);
var newSouthWestPx = new google.maps.Point(southWestPx.x - (50 + menuPadding), southWestPx.y - 50);
var newNorthEast = proj.fromContainerPixelToLatLng(newNorthEastPx);
var newSouthWest = proj.fromContainerPixelToLatLng(newSouthWestPx);
var centerBounds = new google.maps.LatLngBounds(newSouthWest, newSouthWest);
// Decide if any of the new LatLngs are far enough away that the map should move
var shouldMove = false;
var targetBounds = new google.maps.LatLngBounds();
for(var i = 0; i < latLngs.length; i++){
targetBounds.extend(latLngs[i]);
if(!centerBounds.contains(latLngs[i]))
shouldMove = true;
}
// If the LatLngs aren't all near the center of the map, pan to it
if(latLngs.length === 1){
if(shouldMove || map.getZoom() !== 18)
map.panTo(latLngs[0]);
map.setZoom(18);
}else{
var targetZoom = Math.min(getBoundsZoomLevel(targetBounds), 18);
if(shouldMove || map.getZoom() !== targetZoom)
map.panTo(targetBounds.getCenter());
map.setZoom(targetZoom);
}
}
This code should test the "valid" area to make sure all the given LatLngs fit inside, but it doesn't yet make any changes to panTo to "move" the center 200px to the right to account for the 400px worth of content on the left.
The code doesn't work as I intended, but I'm not sure why. I suspect I'm probably doing something wrong when converting from a LatLng to a Point or vice-versa. I may also be doing far more work than is necessary, although I can't think of a way to simplify it.
This turned out to be a pretty simple mistake. In the line var centerBounds = new google.maps.LatLngBounds(newSouthWest, newSouthWest);, I accidentally used the same LatLng twice, rather than the northeast and southwest corners. I also had a couple simple arithmetic issues when calculating newNorthEastPx and newSouthWestPx. Drawing a google.maps.Rectangle using centerBounds helped work that out quickly and easily. For anyone interested, here's the end result:
function panIfNotClose(latLngs, zoomOnly){
if(latLngs.length === 0)
return;
if(typeof latLngs !== "object")
latLngs = [latLngs];
// Calculate the "middle half" of the map
var bounds = map.getBounds();
if(!bounds) // http://stackoverflow.com/a/2833015/802335
return setTimeout(panIfNotClose.bind(this, latLngs, zoomOnly), 10);
var overlay = new google.maps.OverlayView();
overlay.draw = function(){};
overlay.setMap(map);
var proj = overlay.getProjection();
if(!proj)
return setTimeout(panIfNotClose.bind(this, latLngs, zoomOnly), 10);
var center = bounds.getCenter();
var northEast = bounds.getNorthEast();
var southWest = bounds.getSouthWest();
var northEastPx = proj.fromLatLngToContainerPixel(northEast);
var southWestPx = proj.fromLatLngToContainerPixel(southWest);
var menuPadding = 0;
if($navBar.css("display") !== "none")
menuPadding = 400;
var newNorthEastPx = new google.maps.Point(northEastPx.x - 100, northEastPx.y + 100);
var newSouthWestPx = new google.maps.Point(southWestPx.x + (100 + menuPadding), southWestPx.y - 100);
var newNorthEast = proj.fromContainerPixelToLatLng(newNorthEastPx);
var newSouthWest = proj.fromContainerPixelToLatLng(newSouthWestPx);
centerBounds = new google.maps.LatLngBounds(newSouthWest, newNorthEast);
var shouldMove = false;
var targetBounds = new google.maps.LatLngBounds();
for(var i = 0; i < latLngs.length; i++){
targetBounds.extend(latLngs[i]);
if(!centerBounds.contains(latLngs[i]))
shouldMove = true;
}
// If the marker isn't near the center of the map, pan to it
if(latLngs.length === 1){
if(!zoomOnly && (shouldMove || map.getZoom() !== 18))
map.panTo(correctCenter(latLngs[0], proj));
map.setZoom(18);
}else{
var targetZoom = Math.min(getBoundsZoomLevel(targetBounds), 18);
if(!zoomOnly && (shouldMove || map.getZoom() !== targetZoom))
map.panTo(correctCenter(targetBounds.getCenter(), proj));
map.setZoom(targetZoom);
}
}
function correctCenter(latLng, proj){
// $navBar references a jQuery pointer to a DOM element
if($navBar.css("display") === "none")
return latLng;
var latLngPx = proj.fromLatLngToContainerPixel(latLng);
var newLatLngPx = new google.maps.Point(latLngPx.x - 200, latLngPx.y)
return proj.fromContainerPixelToLatLng(newLatLngPx);
}
// Adapted from http://stackoverflow.com/a/13274361/802335
function getBoundsZoomLevel(bounds){
// $mapCanvas is a jQuery reference to the div containing the Google Map
var mapDim = {
width: $mapCanvas.width() * .6,
height: $mapCanvas.height() * .6
};
var ZOOM_MAX = 18;
function latRad(lat){
var sin = Math.sin(lat * Math.PI / 180);
var radX2 = Math.log((1 + sin) / (1 - sin)) / 2;
return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2;
}
function zoom(mapPx, fraction){
return Math.floor(Math.log(mapPx / 256 / fraction) / Math.LN2);
}
var ne = bounds.getNorthEast();
var sw = bounds.getSouthWest();
var latFraction = (latRad(ne.lat()) - latRad(sw.lat())) / Math.PI;
var lngDiff = ne.lng() - sw.lng();
var lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360;
var latZoom = zoom(mapDim.height, latFraction);
var lngZoom = zoom(mapDim.width, lngFraction);
return Math.min(latZoom, lngZoom, ZOOM_MAX);
}

Categories