Mapbox Leaflet zoom does not work from the center - javascript

Problem
I use Mapbox (Leaflet) on a site https://upplevelsekartan.com/djurparker (click Karta). When zooming in it does not zoom in from the middle, but from the upper left corner.
In isolation
The example below uses the exact same code as my site, but this example works as expected. Too see it, run in Full page. When zooming it zooms from the center like it should.
Question
Why does it work perfectly in isolation, but not on my site? How can I fix it?
window.addEventListener('DOMContentLoaded', async() => {
await import('https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js');
mapboxgl.accessToken = 'pk.eyJ1IjoiamVuc3Rvcm5lbGwiLCJhIjoiY2tjb290djIwMG5kZzJzbG1zNGsxeWZ6OSJ9.85qNaU7Xm5Cs2i2yK5tVCw';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
});
map.fitBounds([ // Sweden
[11.0273686052, 55.3617373725],
[23.9033785336, 69.1062472602]
], {padding: 16});
map.on('load', () => {
});
}, false );
div {
height: 300px;
width: 300px;
}
<div id="map"></div>

If you change
.mapboxgl-canvas {
position: absolute;
left: 0;
top: 0;
}
to
.mapboxgl-canvas {
position: relative;
left: 0;
top: 0;
}
it should fix your issue.

Related

Leaflet bindpop() using lazyload of images

I'm using leaflet to put some markers on a map. I have set that, clicking on a marker, a popup will be opened showing an image. Here's a brief example:
var map = L.map('map')
.addLayer(tile)
.setView([initLat, initLon], initZoom);
var m = L.marker([lat, lon])
.bindPopup('<img src="1.jpg"/>')
.addTo(map);
My objective is to load those images ("1.jpg" in the example above) using lazy load, so it's only loaded when I click on the marker.
Does anyone knows how to do this?
Thanks!
You could set the content of the popup when the popup is opened.
Let's create a custom popup with a lazyload option and without content :
var m = L.marker([0, 0])
.bindPopup(L.popup({
lazyload: '<img src="1.jpg"/>'
}))
.addTo(map);
You can then set a global handler to fill your popup when needed:
map.on('popupopen', function(e) {
var popen = e.popup;
if (popen.options.lazyload) {
popen.setContent(popen.options.lazyload);
}
});
And a demo:
var map = L.map('map', {
center: [0, 0],
zoom: 1
});
var m = L.marker([-30, 0])
.bindPopup(L.popup({
lazyload: '<img src="https://i.stack.imgur.com/shfxy.jpg?s=32&g=1" />'
}))
.addTo(map);
map.on('popupopen', function(e) {
var popen = e.popup;
if (popen.options.lazyload) {
popen.setContent(popen.options.lazyload);
}
});
html, body {
height: 100%;
margin: 0;
}
#map {
width: 100%;
height: 150px;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/leaflet.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/leaflet.js"></script>
<div id='map'></div>
Actually in the case where you fill your popup with a String content (like you did with .bindPopup('<img src="1.jpg"/>'), Leaflet converts it (through innerHTML) to DOM nodes only when the Popup is first opened on a map. Therefore your image will be loaded only at that moment, which is exactly the lazy loading behaviour you are looking for.
So you do not need to do anything extra from what you already have done in your question code:
(make sure you refresh your page / clear your cache to see the image loading pass in the browser network requests)
var map = L.map('map', {
center: [0, 0],
zoom: 1
});
var m = L.marker([-30, 0])
.bindPopup('<img src="https://i.stack.imgur.com/shfxy.jpg?s=32&g=1" />')
.addTo(map);
html, body {
height: 100%;
margin: 0;
}
#map {
width: 100%;
height: 150px;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/leaflet.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/leaflet.js"></script>
<div id='map'></div>

Make left side image sticky while right side scrolls with Wordpress and Waypoints

I'm designing a website with Wordpress and the Divi theme. I would like there to be a 2 column section with an image on the left, and text on the right. When the image gets to the top of the viewport, I'd like the image to be be 100% of the viewport height and stick, while the right side continues to scroll until the next section.
http://newbcs.bigcityswing.com/groupclasstest
Here is what I have so far:
<script type="text/javascript">
(function($) {
if ( $.fn.waypoint ) {
var $waypoint_selector,
$waypoint_selector = $('#top-section'),
$site_header = $('#main-header'),
$fixed_column = $('#stickySection .stickyColumn1'),
$non_fixed_column = $('#stickySection .unstickyColumn2'),
$offset = $site_header.height() +
$waypoint_selector.height();
console.log($offset);
$waypoint_selector.waypoint( {
handler : function( direction ) {
if ( direction === 'down' ) {
$fixed_column.addClass( 'wpc-fixed-column' );
$non_fixed_column.addClass('wpc-non-fixed-column')
console.log('down');
} else {
$fixed_column.removeClass( 'wpc-fixed-column' );
$non_fixed_column.removeClass('wpc-non-fixed-column')
console.log('up');
}
},
offset: -$offset
} );
}
})(jQuery)
And CSS:
.wpc-fixed-column {
position: fixed;
top: 0;
}
.unstickyColumn2.wpc-non-fixed-column {
left: 33.333%;
}
I can't get anything to happen, and when I look at the console in Chrome, I get this:
Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080
Any help would be really appreciated.
Thanks
css:
.stickyColumn1 {
position: fixed;
top: 0;
left: 0;
z-index: 1;
}
.unstickyColumn2 {
width: 100%;
padding-left: 33.4%;
}

Forcing a layer repaint

I have a kiosk application running on Ubuntu server 14.04.3 and chrome. Currently I have some code which hides the mouse if there was no movement for 2 seconds and once the user attempts to move the mouse again it shows up again. The trick is by using a cursor:none and adding an overlay:
js:
var body = $('body');
function hideMouse() {
body.addClass("hideMouse");
body.on('mousemove', function(){
if(window.hiding) return true;
window.hiding = true;
body.removeClass("hideMouse");
$('div.mouseHider').remove();
clearTimeout(window.hideMouse);
window.hideMouse = setTimeout(function(){
body.addClass("hideMouse");
$('<div class="mouseHider"></div>').css({
position: 'fixed',
top: 0,
left: 0,
height: '100%',
width: '100%',
zIndex: 99999
}).appendTo(body);
redraw(document.body);
setTimeout(function(){
window.hiding = false;
}, 100);
}, 4000);
});
}
function redraw(e) {
e.style.display = 'none';
e.offsetHeight;
e.style.display = 'block';
}
css:
body.hideMouse *, body.hideMouse{
cursor: none;
}
body.hideMouse *{
pointer-events: none !important;
}
This code works perfectly fine but there is only 1 caveat. When the page first loading it attempts to hide the mouse with the same trick but the mouse is still sticking there since it just didn't repainted the layer I guess. If I want it to work, I have to move the mouse a little bit and from then on it will work as expected and hide the mouse. The thing is that the kiosk application is restarting every day which means I boot the X display again and the mouse is being reset to the middle of the screen and it just sticks there until I move it a little bit. I hope you understand what I mean.
Do you guys have any idea how I can fix this?
You don't need all that code to do what you want. You could do:
Create a setTimeout to hide the cursor after 2s as soon as the page is loaded
When someone moves the mouse, you:
2.1. Show the cursor again
2.2. Clear the current setTimeout
2.3. And create the setTimeout to hide the cursor after 2s again.
The code below should work for you:
document.addEventListener('DOMContentLoaded', function() {
var cursorNone = document.getElementById('cursor-none');
var t = setTimeout(hideMouse, 2000);
document.addEventListener('mousemove', function(e) {
showMouse();
clearTimeout(t);
t = setTimeout(hideMouse, 2000);
});
function hideMouse() {
cursorNone.classList.remove('hidden');
}
function showMouse() {
cursorNone.classList.add('hidden');
}
});
#cursor-none {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
cursor: none;
background-color: transparent;
}
.hidden {
display: none;
}
<body>
<div id="cursor-none" class="hidden"></div>
</body>

JS Maps v3: custom marker with user profile picture

I am struggling since 2 days with something I was thinking easy, on a map, I have to display a marker for each user with the user FB profile picture inside.
I am wondering how I can have a result similar to this one? What I tried was really hackish.
I put the FB picture as the marker icon
I put a CSS class on the label of the marker
I find the sibling to add this border and this arrow to decorate the user picture
but it doesn't work when there is more than one marker on the map.
.marker-labels {
display: none !important;
+ div {
background-color: $dark-gray;
border: 2px solid $dark-gray;
#include radius(0.2em);
height: 54px !important;
width: 54px !important;
overflow: inherit !important;
> img {
height: 50px;
width: 50px;
}
&:after {
content: ' ';
height: 0;
width: 0;
border: 6px solid transparent;
border-top-color: $dark-gray;
position: absolute;
top: 52px;
left: 19px;
}
}
}
global question:
how can I get an icon like that (http://mt-st.rfclipart.com/image/thumbnail/24-1d-5f/blue-glossy-square-map-pin-or-speech-bubble-Download-Royalty-free-Vector-File-EPS-29153.jpg for instance) with a custom user picture inside? is it possible?
otherwise how is it possible to customize the icon (if it is the profile picture) to have a result similar to the screenshot
thanks for your help
This answer assumes you already have the URIs for the facebook profile images. Honestly, it feels there is an easier way, but I found some code that shows how to create a custom marker with custom HTML elements and I went from there. From there's it's pretty easy to create a custom marker that accepts a image URI as a parameter. From the original, I just added an imageSrc parameter, moved the styling outside the code by attaching a class name to the new div. In terms of html and css, I just appended an image with the passed image URI into the div, and just added some CSS to make it look like what you have.
Demo
So the javascript code looks something like this:
function CustomMarker(latlng, map, imageSrc) {
this.latlng_ = latlng;
this.imageSrc = imageSrc; //added imageSrc
this.setMap(map);
}
CustomMarker.prototype = new google.maps.OverlayView();
CustomMarker.prototype.draw = function () {
// Check if the div has been created.
var div = this.div_;
if (!div) {
// Create a overlay text DIV
div = this.div_ = document.createElement('div');
// Create the DIV representing our CustomMarker
div.className = "customMarker" //replaced styles with className
var img = document.createElement("img");
img.src = this.imageSrc; //attach passed image uri
div.appendChild(img);
google.maps.event.addDomListener(div, "click", function (event) {
google.maps.event.trigger(me, "click");
});
// Then add the overlay to the DOM
var panes = this.getPanes();
panes.overlayImage.appendChild(div);
}
// Position the overlay
var point = this.getProjection().fromLatLngToDivPixel(this.latlng_);
if (point) {
div.style.left = point.x + 'px';
div.style.top = point.y + 'px';
}
};
CustomMarker.prototype.remove = function () {
// Check if the overlay was on the map and needs to be removed.
if (this.div_) {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
}
};
CustomMarker.prototype.getPosition = function () {
return this.latlng_;
};
I think I added only one or two lines here. You can just add this to your page I think. With this in place you can just style the container as normal, and it should apply to all the custom markers. You can add elements and classes as you see fit to achieve the look you are looking for. But for completion's sake I added the styles I used for the demo here.
.customMarker { /* the marker div */
position:absolute;
cursor:pointer;
background:#424242;
width:100px;
height:100px;
/* we'll offset the div so that
the point passed doesn't end up at
the upper left corner but at the bottom
middle. so we'll move it left by width/2 and
up by height+arrow-height */
margin-left:-50px;
margin-top:-110px;
border-radius:10px;
padding:0px;
}
.customMarker:after { //triangle
content:"";
position: absolute;
bottom: -10px;
left: 40px;
border-width: 10px 10px 0;
border-style: solid;
border-color: #424242 transparent;
display: block;
width: 0;
}
.customMarker img { //profile image
width:90px;
height:90px;
margin:5px;
border-radius:2px;
}
And for the demo I have some sample data in array and placed them on the map using a for loop.
var data = [{
profileImage: "http://domain.com/image1.jpg",
pos: [37.77, -122.41],
}, {
profileImage: "http://domain.com/image2.jpg",
pos: [37.77, -122.41],
}]
for(var i=0;i<data.length;i++){
new CustomMarker(
new google.maps.LatLng(data[i].pos[0],data[i].pos[1]),
map,
data[i].profileImage
)
}
I hope that helps.

Openlayers / Openstreetmap background is vertically striped and squished

So the background of my openlayers implementation appears to be squished into vertical stripes. The weird thing is that it wasn't always like this but even when I stash all my changes back to a point where I know it was working, it still is broken. It makes me wonder if perhaps something has changed about the way the tile assets are being delivered. I have tried switching between using osm and wms layers with no change, any help would be appreciated.
Here is the pertinent code:
initMap: function() {
var view = this;
var map = this.map = new OpenLayers.Map();
map.render(this.$map[0]);
var wmsLayer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
"http://vmap0.tiles.osgeo.org/wms/vmap0?", {layers: 'basic'});
var osmLayer = new OpenLayers.Layer.OSM();
this.layers = {
point: new OpenLayers.Layer.Vector("Point Layer"),
line: new OpenLayers.Layer.Vector("Line Layer"),
polygon: new OpenLayers.Layer.Vector("Polygon Layer")
};
this.setValue(this.value);
map.addLayers([this.layers.point, this.layers.line, this.layers.polygon, osmLayer]);
drawControls = {
point: new OpenLayers.Control.DrawFeature(this.layers.point,
OpenLayers.Handler.Point),
line: new OpenLayers.Control.DrawFeature(this.layers.line,
OpenLayers.Handler.Path),
polygon: new OpenLayers.Control.DrawFeature(this.layers.polygon,
OpenLayers.Handler.Polygon)
};
this.layers[this.layerType].events.on({'sketchcomplete': function(feature) {
if (!view.multiple) {
// deactivate polygon layer once a polygon has been added
drawControls[view.layerType].deactivate();
}
}});
for(var key in drawControls) {
map.addControl(drawControls[key]);
}
if (this.layers[this.layerType].features.length) {
var bounds = this.layers[this.layerType].getDataExtent();
var zoom = this.layers[this.layerType].getZoomForExtent(bounds);
var lon = (bounds.top - bounds.bottom) / 2;
var lat = (bounds.right - bounds.left) / 2;
map.setCenter(new OpenLayers.LonLat(lon,lat), 3);
map.zoomToExtent(bounds);
if (view.multiple) {
drawControls[view.layerType].activate();
}
} else {
map.setCenter(new OpenLayers.LonLat(-11174482.03751,4861394.9982606), 4);
drawControls[view.layerType].activate();
}
this.$('.clear').click(function(e) {
e.preventDefault();
view.layers[view.layerType].destroyFeatures();
drawControls[view.layerType].activate();
});
},
Here is the output:
So I found the problem. Twitter bootstrap has a line in its reset file that sets:
img { max-width: 100% }
This was squishing the images. You can fix it by doing:
img { max-width: none; }
I was having the same problem, are you using bootstrap of twitter?
I found out there was a selector for img elements that was affecting the map. I had the next selector into the bootstrap.css"
img {
max-width: 100%;
vertical-align: middle;
border: 0;
-ms-interpolation-mode: bicubic;
}
I don't know whay the parameter max-width: 100%; makes the vertical stripes in my case. I remove the propertie max-width: 100% and now is working.
Setting img { max-width: 100% } to img { max-width:none; } does resolve the problem.
However this will have an unintended effect on images throughout your website. I am also using Bootstrap Twitter's carousel component for images and when I made the above changed the images did not fit in the carousel. Therefore I changed it so that it only targets the images in the OpenLayers / OpenStreetMap div.
div#outlet_map img { max-width:none; }

Categories