Positioning custom markers on a Google Map - javascript

I have the following Google Map test app: http://dev.driz.co.uk/googlemap/
As you can see I use geolocation to show your (the user) position on the map, and then some JSON data to populate the map with other markers.
Note: depending where you are in the world you may not see the pins (they are in the UK near Huddersfield) if you zoom out you should seem them.
I am having the following issues:
1.) All the markers have the same titles, so I'm presuming that somewhere in the for loop at the bottom of the page I have made a mistake... Not sure what though?
Fixed in answers below.
2.) The markers have various overlapping issues due to the z-index and also because some of the markers have the same co-ordinates. Is it possible to make it so that markers offset themselves a couple pixels per loop so that they don't overlap, and the z-index automatically increases per loop so they are higher than the previous marker
Need to make it so that when a user hovers the marker it has a higher z-index to make it sit on top... If that makes sense? So in the hover event I need to get the latest offset and then add to that to make it the highest! But how do I alter the zindex of the marker on the hover?
3.) The final thing (and probably the most difficult) is that the tooltips are not equally positioned when moved to the right side of the marker when the map is moved. Any ideas to improve this? They get even worse with the JSON-based markers and slip off the map.
Can anyone help me out with these problems?
Thanks

I don't know if this will work, but its following the pattern of that link I shared, perhaps something like this....
function doToolTip(item) {
return function () {
mTooltip = new Tooltip('<span class="name">' + item.User.Profile.firstname + ' asked:</span> <span class="title">' + item.Post.title + '</span>');
mTooltip.open(this.getMap(), this);
};
}
...and this is your main code. I think 'item needs' initialising outside the scope of the loop (but I could be wrong)
//other code etc...
var item;
for (var i = 0; i < data.length; i++) {
item = data[i];
//other code etc....
google.maps.event.addListener(marker, 'mouseover', doToolTip(item));
//other code etc...
}
OK. I'm guessing here, as I haven't got a local copy of the code, but, It looks like you need to change the z-index when you do the draw function...
//code fragment...
// need to force redraw otherwise it will decide to draw after we show the Tooltip
$(this).css('z-index', 9999);
this.draw();
// show tooltip
With regard to the position of the tooltip, you're going to have to experiment with the draw function, as it seems to calculate the position from the marker. It might be better to work out the position not from the google map coordinates but from the actual position on the page - I think the culprits are:
pos = this.getProjection().fromLatLngToDivPixel(this.get('position'));
// top offset
top = pos.y - this.getAnchorHeight() / 2 - this.wdiv.outerHeight() / 2;
// left offset
if (this.getMap().getCenter().lng() > this.get('position').lng()) {
left = pos.x + this.wdiv.outerWidth();
} else {
left = pos.x - this.wdiv.outerWidth();
}
// window position
this.wdiv.css('top', top);
this.wdiv.css('left', left);
If the positioning is consistently off, you could just apply a correction to the top and left values, if it's more complicated, you'll have to change the algorithm.

Related

How to move an unknown object to center screen? HTML/CSS/JS

I am developing a VueJS project and have created a set of cards appearing on the page, when one of these cards is selected, I wish for it to move to centre screen but keep the position it has moved from in the list of options.
I know that by changing the position from 'unset' to 'relative' the card now has move functionality with 'left', 'top' etc. but I still need to find a way to automatically move the card to centre screen regardless of where on the screen the card is moving from.
Does anyone know how to achieve this with the use of JS?
I imagine there is a way of receiving the current location of the node and moving it to the center of the screen, but I am not sure on the specifics of how to achieve it...
Image for context:
CardsProject
EDIT: I have for now gone with rendering an absolute position for the card which means there's no CSS transition from the card's original place to the centre of the screen and the card also temporarily loses its place within the deck.
Before click: click here for image
After click: click here for image
I found the answer after many, many hours of scouring the internet and deepfrying my code.
The answer: Don't use 'relative' positioning!
There's a far nice option to hold the position the element is moving from, but allow for the item to move freely with the use of CSS' top or left etc. and this option is position:sticky;!
With this and the use of JavaScript's coordinates documentation
.getBoundingClientRect()
...I managed to solve the mystery. The function I made to pull a vector between the current object and it the centre of the screen can be found here, returning an array of size 2 of X and Y vectors respectively.
function calcCenterMove(element){
/*
X and Y are the current position of the element to be moved (top left corner).
Width and Height are the width and height of the element to be moved.
CX and CY are the X and Y coordinates of the centre of the screen.
*/
var x = element.getBoundingClientRect().x;
var y = element.getBoundingClientRect().y;
var width = element.getBoundingClientRect().width;
var height = element.getBoundingClientRect().height;
var cx = window.innerWidth / 2;
var cy = window.innerHeight / 2;
var xVector = cx-(width/2)-x;
var yVector = cy-(height/2)-y;
return [xVector, yVector];
}
var xAxisMove = calcCenterMove(element)[0];
var yAxisMove = calcCenterMove(element)[1];
element.style = "transform: translate("+xAxisMove+"px,"+yAxisMove+"px);";
I have paired the above code with a z-index to place the element above all others, and a screen dimming cover, to prevent the user from scrolling elsewhere or interacting with any other options.
Issues still arise here if the user resizes the screen, but I believe that is a different issue to address, possibly by using an event listener to assess a window resize and translate the element from the previous centre to the new centre using the same cx and cy properties above (or perhaps even the entire function!).
Nevertheless, I have come to the answer I was looking for, anyone feel free to use the code above, if needed!
Here are images for reference:
Before click
After click
Regards!

Align non-geographical leaflet map maxBounds to the bottom of the container

I am creating a non-geographical map with leaflet, which shows a ski resort.
JSFiddle: https://jsfiddle.net/exophunk/ruzgeqL4/
I am using map.fitBounds(bounds) to fit the map to the container and map.setMaxBounds(bounds) to make sure you can't pan outside the map.
This works perfect when zooming in, but as long as the map is smaller than the viewport height, I would like to "align" the whole thing to the bottom of the container, so the map never moves away from the bottom of the container. As it is a mountain, it is pretty obvious.
I achieved this by adding an offset to the maxBounds, as you can see in this example (blue box = max bounds):
https://jsfiddle.net/exophunk/05cq3rzt/
The problem with this approach is, that you can now pan into the "empty sky" when zooming in, while I would actually like to keep the maxBounds as in the first example, so it also restricts movement upwards.
I think this would mean I would need to resize the max bounds while zooming in maybe? But there, I messed up coordinate systems and containers and wasn't able to do this properly.
How can I make sure the map always sticks to the container bottom while keeping the original max bounds, if possible?
You can overwrite the default wheel zoom and check if the zoom mousepoint is in the bounds, else zoom to the center of the bounds/map:
map.scrollWheelZoom._performZoomOrg = map.scrollWheelZoom._performZoom;
map.scrollWheelZoom._performZoom = function(e){
var mouse = map.scrollWheelZoom._lastMousePos;
var llpixel = map.containerPointToLatLng(mouse)
if(!bounds.contains(llpixel)){
map.scrollWheelZoom._lastMousePos = map.unproject([container.clientWidth/2, mapHeight]);
}
map.scrollWheelZoom._performZoomOrg();
}
https://jsfiddle.net/falkedesign/c04ngftj/

Kinetic js how to scale layer in a stage?

I want to add zoom feature my app . I use Kinetic js and somewhere I found solutions for this feature but I can't apply these solution for some reason . I tried to adapt the solutions but unsuccesful . I have many Kinetic.Layer , some of them will scale when zooming apply. my challenge is that : zoom will happen on mouse position . solution that I found gives me : layer.setPosition() after scaling . As I mentioned before , I must not use "layer.setPosition" I will do this as using stage.setPosition() but I couldn't calculate new x and y of position 100% accurately. Could anyone suggest me any solution way ?
What you really want to do when zooming is to set the scale.
You can set the scale for any layer, node, or the entire stage. Just do:
layer1.setScale(2,2); // this doubles the layer size from the original
This doesn't affect any other layer, so your overlay will stay in place.
In addition, you should also do:
layer1.setPosition(x,y); // this will move the layer to the fixed point you want.
All together you could do:
function zoom(){
var position = stage.getUserPosition();
layer1.setScale(2,2);
layer1.setPosition(position.x - layer2.getX(), position.y - layer2.getY()); //move the layer by an offset based on the second layer. This isn't exactly correct so it's something you have to experiment with.
}
Check out this: http://jsfiddle.net/TFU7Z/1/ Maybe is what you are looking for, I did not quite understand the question.
var zoom = function(e) {
var zoomAmount = 1;
layer.setScale(layer.getScale().x+zoomAmount)
layer.draw();
}
document.addEventListener("click", zoom, false)
Just click anywhere to zoom. You can attach the "click" event listener to whatever part of the stage / document you want.
These answers seems not to work awith the KineticJS 5.1.0. These do not work mainly for the signature change of the scale function:
stage.setScale(newscale); --> stage.setScale({x:newscale,y:newscale});
However, the following solution seems to work with the KineticJS 5.1.0:
JSFiddle: http://jsfiddle.net/rpaul/ckwu7u86/3/

Google maps V3 custom marker images and fitBounds()

I am trying to get my custom markers to show up on my map after i have used the fitBounds() method to fit the boundaries of the map to the markers themselves.
I have done this by looping over the markers array and then extending the boundaries of the map to incorporate the marker co-ordinates.
This works fine with the stock google maps markers. However, my client wants to use quite large (36px by 57px) marker images for their site. How do i compensate for this when fitting the boundaries of the map?
At the moment when using the custom marker images they do not all fit inside the boundaries set.
Since you already have calculated the bounds, you may just need to extend the bounds to add enough buffer area to include the large images. The formula you can use to calculate or extend a bounds this way is called a convex hull; the Computational Geometry Algorithms Library has a section on 2D Convex Hull Algorithms or there is a JavaScript Quickhull Article that also includes a nifty online example near the bottom of the page. Hope this is helpful -
The cheap answer is to always zoom out one level after fitBounds(). But we can do a bit better.
I like writing hacks. Here I am making the assumption that the size of your marker will never be larger than 36x57. I tested a while back to find that fitBounds() leaves a margin of around 42 px between the edge and the closest marker (maybe not on mobiles), and I'm also assuming you are not repositioning the marker, that is, it will always be displayed above the given coordinate position. If icons run off to the other sides, adjustments are needed.
My hack takes advantage of a function that measures the pixel position of a LatLng (using the container version, I read here that the div version is not reliable with bounds changes).
Since we know the height of the icon, and where the topmost marker is, we can pan the map south a bit if it's determined to be offscreen. In case there's not enough margin below, the only option is to zoom out. My only concern is it will be jerky because it calls for two events: fitBounds and the custom panning/zooming. The only answer then would be to rewrite a custom fitBounds. When I tested manually the events ran smoothly.
http://jsfiddle.net/sZJjY/
Click to add cat icons, right-click to trigger the resize/panning.
Example: place 3-4 kitties, right-click, then purposely place another that goes off the top, right-click again.
function fitIcons() {
var left = 180.0;
var right = -180.0;
var top = -90.0;
var bottom = 90.0;
for (var i = 0; i < markers.length; i++) {
curlat = markers[i].getPosition().lat();
curlng = markers[i].getPosition().lng();
if(curlat > top) { top = curlat; }
if(curlat < bottom) { bottom = curlat; }
if(curlng > right) { right = curlng; }
if(curlng < left) { left = curlng; }
}
var overlay = new google.maps.OverlayView();
overlay.draw = function() {};
overlay.setMap(map);
map.fitBounds(new google.maps.LatLngBounds(
new google.maps.LatLng(bottom, left),
new google.maps.LatLng(top, right)));
topPixels = overlay.getProjection().fromLatLngToContainerPixel(
new google.maps.LatLng(top, right));
bottomPixels = overlay.getProjection().fromLatLngToContainerPixel(
new google.maps.LatLng(bottom, left));
topGap = topPixels.y;
bottomGap = $("#map_canvas").height() - bottomPixels.y;
if(topGap < iconHeight) {
if(bottomGap > iconHeight) {
map.panBy(0, topGap);
}
else {
map.setZoom(map.getZoom() - 1);
}
}
}

Center Google Maps (V3) on browser print

I have a map set to 100% of the page width. The map has one marker and is centered on that marker. When I print the browser, I want the map to stay centered on the marker. This is the code I wrote to do so:
var lastPos = map.getCenter();
google.maps.event.addListener(map, "idle", function() {
lastPos = map.getCenter();
console.log(lastPos.toString());
});
google.maps.event.addDomListener(window, "resize", function() {
google.maps.event.trigger(map, "resize");
map.setCenter(lastPos);
console.log("Re-center on " + lastPos.toString());
});
This works when I re-size my browser, but does not work when the browser re-sizes itself before printing. If my browser is above a certain width then the marker is shifted entirely off the page (to the right) when the map is printed.
Here is my test case: http://www-sf.talispoint.com/testmapprint.html
You would need to add a #media print and give the map the size when printing and then you can do what is explained below.
When a map is printed what happens is that the top left corner is kept and the map is adjusted to fit the #media print size.
If you want the center to stay the same you need to manually change the center of the map.
http://jsbin.com/owiwox/33 is an example on how to work around this.
It uses a listener for the print media being applied event and adjusts the center of the map using a ratio on how the map is changed (made smaller)
One thing that you have to take care of is that you might have to make this browser targeted to make it work on all browsers (This solution works well in Chrome)
A good resource for making it work across browsers is:
http://tjvantoll.com/2012/06/15/detecting-print-requests-with-javascript/
The js code from the sample above listens to print requests and shifts the map so that the top left corner has the same center as the big map.
To cut the whole story short, this is how it works
You need to put in the ratio of the map vs printed map (or get the size by checking it from JS)
You assign it to:
var widthRatio = 2;
var heightRatio = 3;
You listen for print media being applied and shift the center so that it does not change
After you finish print , you revert the change.
Still here you have the problem that a part of the map will be cut, but there is not a good solution here, since the zoom level -1 tiles might not be cached so when you zoom out to fit bounds you might get no tiles.
It seems the problem with your 'printing' or 'printer'.
I did a test:
load the test map and make the browser very wide
print preview and saw the problem you described
But: I can change the printing scale from 'Shrint to fit' (default for IE and FF) to say 30% and was able to print the map as seen on the screen.
Another thought is:
You may try to use another CSS for print to limit the map div width, but I am not sure if that will trigger the resize of the map first (you may refer to this post: Javascript Event Handler for Print)

Categories