MapBox markers Move on zooming - mapbox

I'm working on MapBoxgl and I want to add several markers.
Here is my index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<link href=" /assets/css/bootstrap.min.css " rel="stylesheet" />
<link href=" /assets/css/mapbox-gl.css " rel="stylesheet" />
<link href=" /assets/css/main.css " rel="stylesheet" />
</head>
<body>
<div id="map"></div>
<script src="/assets/js/mapbox-gl.js"></script>
<script src="/assets/js/map-style.js"></script>
</body>
</html>
This is map-style.js:
var map = new mapboxgl.Map({
container: 'map',
center: [57.3221, 33.5928],
zoom: 5,
style: style
});
var geojson = {
type: 'FeatureCollection',
features: [{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [30.61, 46.28]
},
properties: {
title: 'point 1',
description: 'point 1 Description',
message: 'point1',
iconSize: [25, 25]
}
},
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [30.62, 46.2845]
},
properties: {
title: 'point 2',
description: 'point 2 Description',
message: 'point 2',
iconSize: [25, 25]
}
}]
};
map.on('load', function () {
// add markers to map
geojson.features.forEach(function(marker) {
// create a DOM element for the marker
var el = document.createElement('div');
el.className = 'markers';
el.style.backgroundImage = 'url(assets/marker-azure.png)';
//el.style.width = marker.properties.iconSize[0] + 'px';
el.style.height = marker.properties.iconSize[1] + 'px';
el.addEventListener('click', function() {
window.alert(marker.properties.message);
});
// add marker to map
new mapboxgl.Marker(el)
.setLngLat(marker.geometry.coordinates)
.addTo(map);
});
});
And following is main.css portion related to map and marker:
#map { position:absolute; top:0; bottom:0; width:100% }
.markers {
display: absolute;
border: none;
border-radius: 50%;
cursor: pointer;
padding: 0;
}
So, my problem is that when I add width property to markers, their icon be displayed correctly (with a bit stretch) but they are in wrong coordinate and move on zoom, like picture below:
On the other hand, if width property is eliminated, they are in right place and does not move on zooming, but they are very stretched and in fact as wide as screen (following image):
It's noteworthy that I've used bootstrap. Can it be the reason of this? If not, what's the problem?
Thanks

import 'mapbox-gl/dist/mapbox-gl.css';
Adding import css works for me.

I found the solution and share it here with others who will encounter this problem. The problem caused because of using a not-most-recent version of the library. After upgrading to the latest release, I could get rid of that problem.
Note that these kinds of problems sometimes occur, when you use npm. Make sure that the library is downloaded completely and It's the latest release.
Latest releases of MapBox can be found at here.

had similar issue, the marker seemed to change position on zoom in/out, fixed it by setting offset, thought to share if it can help others, here is the fiddle
// add marker to map
var m = new mapboxgl.Marker(el, {offset: [0, -50/2]});
m.setLngLat(coordinates);
m.addTo(map);

(Using mapbox 1.13.0)
I had a similar issue where the popups weren't displaying and would change position based on zoom.
Mapbox officially states that you need to include the css file to have markers and popups work as expected.
https://docs.mapbox.com/mapbox-gl-js/guides/install/
HOWEVER, you can also copy that css directly into a component and use that as an element. For example:
export default function MarkerMapTest(props) {
const mapContainer = React.useRef(null)
const map = React.useRef(null)
const elemRef = React.useRef(null)
React.useEffect(() => {
map.current = new mapboxgl.Map({
container: mapContainer.current,
style: "mapbox://styles/mapbox/dark-v10",
center: [151.242, -33.9132],
zoom: 14,
})
const marker = new mapboxgl.Marker({
element: elemRef.current,
})
.setLngLat([151.242, -33.9132])
.addTo(map.current)
}, [])
return (
<div
style={{ width: 600, height: 600, backgroundColor: "gray" }}
ref={mapContainer}
>
<div
style={{
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: "red",
position: "absolute", // !
top: 0, // !
left: 0, // !
}}
ref={elemRef}
/>
</div>

In my case the svg I used had some space around the real content. That way it seemed for me that the marker was moving. A simple fix was to remove the space around the content (e.g. with the "Resize page to drawing or selection" option of inkscape: https://graphicdesign.stackexchange.com/a/21638)
Also it is important to set display: block on the svg-marker. See: https://stackoverflow.com/a/39716426/11603006

Related

How to add I3SLoader 3dtile layer (deck.gl) integrated with mapbox

I am trying to add arcgis 3d layer in mapbox with DeckGL. But I have this error (Uncaught TypeError: Tile3DLayer is not a constructor)
If I could bring data from arcgis sceneserver ,I try add two maps like compare(mapbox gl swipe tool) them.
maybe there are different ways to solve this problem. Taking the arcgis data I need and calling two maps on the same web page. Then make a presentation with the comparison tool between these maps.
<html>
<head>
<title>deck.gl + Mapbox Integration</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src="https://unpkg.com/deck.gl#^6.2.0/deckgl.min.js"></script>
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v0.50.0/mapbox-gl.js"></script>
<link rel="stylesheet" type="text/css" href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.50.0/mapbox-gl.css">
<script src="https://unpkg.com/#loaders.gl/i3s#2.1.6/dist/dist.min.js"></script>
</head>
<body>
<div id="map" style="width: 100vw; height: 100vh"></div>
</body>
<script type="text/javascript">
const { MapboxLayer, ScatterplotLayer } = deck;
// Get a mapbox API access token
mapboxgl.accessToken = 'pk.eyJ1IjoidWJlcmRhdGEiLCJhIjoiY2pudzRtaWloMDAzcTN2bzN1aXdxZHB5bSJ9.2bkj3IiRC8wj3jLThvDGdA';
// Initialize mapbox map
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v9',
center: [-74.50, 40],
zoom: 9
});
const {DeckGL, Tile3DLayer} = deck;
const {I3SLoader} = loaders;
const deckgl = new DeckGL({
controller: true,
layers: [
new Tile3DLayer({
id: 'tile-3d-layer',
data:
'https://tiles.arcgis.com/tiles/z2tnIkrLQ2BRzr6P/arcgis/rest/services/SanFrancisco_Bldgs/SceneServer/layers/0',
loader: I3SLoader,
onTilesetLoad: (tileset) => {
const {zoom, cartographicCenter} = tileset;
const [longitude, latitude] = cartographicCenter;
const viewState = {
...deckgl.viewState,
zoom: zoom + 2.5,
longitude,
latitude
};
deckgl.setProps({initialViewState: viewState});
}
})
]
});
map.on('load', () => {
map.addLayer(deckgl);
});
</script>
</html>
I'm using React, the core code is here, hope I can help you.
// import { MapboxLayer } from '#deck.gl/mapbox';
// import {I3SLoader} from '#loaders.gl/i3s';
// import { Tile3DLayer } from '#deck.gl/geo-layers';
// import mapboxgl from 'mapbox-gl';
mapboxgl.accessToken = 'your-token';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: [-122.51473530281777,37.70463582140094],
zoom: 13
});
const layer = new MapboxLayer({
id:'i3s',
type: Tile3DLayer,
data: 'https://tiles.arcgis.com/tiles/z2tnIkrLQ2BRzr6P/arcgis/rest/services/SanFrancisco_Bldgs/SceneServer/layers/0',
loader: I3SLoader
});
map.on('load', () => {
map.resize();
map.addLayer(layer);
});

Leaflet zooms so now and than too much on clicking a cluster

Leaflet zooms so now and than too much on clicking a cluster.
A small part of 1 marker is shown on the right and a small part from the other marker is shown on the left side of the map.
My workaround is counting markers in scope and re-adjust the zoomlevel when the counter = 0.
Shouldn't that be in the zoomcalculation of leaflet?
The zoom correction:
mcg.on('clusterclick', function () {
if (markersInScope == 0) {
var zoom = map.getZoom();
map.setZoom(zoom - 1);
}
});
You could try adjusting the zoom to fit the bounds of the mcg and add some padding
mcg.on('clusterclick', function () {
map.fitBounds(mcg.getBounds().pad(0.5));
}
});
If you add the markers to a featureGroup, the bounds for the group will available to you:
Here is a sample file from Leaflet:
<!DOCTYPE html>
<html>
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="../../dist/leaflet.css" />
<link rel="stylesheet" href="../css/screen.css" />
<script src="../leaflet-include.js"></script>
</head>
<body>
<div id="map" style="width: 600px; height: 600px; border: 1px solid #ccc"></div>
<button onclick="geojsonLayerBounds();">Show GeoJSON layer bounds</button>
<button onclick="featureGroupBounds();">Show feature group bounds</button>
<script src="geojson-sample.js"></script>
<script>
var osmUrl = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
osmAttrib = '© OpenStreetMap contributors',
osm = L.tileLayer(osmUrl, {maxZoom: 18, attribution: osmAttrib}),
rectangle,
featureGroup;
var map = new L.Map('map', {
center: new L.LatLng(0.78, 102.37),
zoom: 7,
layers: [osm]
});
var geojson = L.geoJson(geojsonSample, {
style: function (feature) {
return {color: feature.properties.color};
},
onEachFeature: function (feature, layer) {
var popupText = 'geometry type: ' + feature.geometry.type;
if (feature.properties.color) {
popupText += '<br/>color: ' + feature.properties.color
}
layer.bindPopup(popupText);
}
});
geojson.addLayer(new L.Marker(new L.LatLng(2.745530718801952, 105.194091796875)))
var eye1 = new L.Marker(new L.LatLng(-0.7250783020332547, 101.8212890625));
var eye2 = new L.Marker(new L.LatLng(-0.7360637370492077, 103.2275390625));
var nose = new L.Marker(new L.LatLng(-1.3292264529974207, 102.5463867187));
var mouth = new L.Polyline([
new L.LatLng(-1.3841426927920029, 101.7333984375),
new L.LatLng(-1.6037944300589726, 101.964111328125),
new L.LatLng(-1.6806671337507222, 102.249755859375),
new L.LatLng(-1.7355743631421197, 102.67822265625),
new L.LatLng(-1.5928123762763, 103.0078125),
new L.LatLng(-1.3292264529974207, 103.3154296875)
]);
map.addLayer(eye1).addLayer(eye2).addLayer(nose).addLayer(mouth);
featureGroup = new L.FeatureGroup([eye1, eye2, nose, mouth]);
map.addLayer(geojson);
map.addLayer(featureGroup);
function geojsonLayerBounds() {
if (rectangle) {
rectangle.setBounds(geojson.getBounds());
} else {
rectangle = new L.Rectangle(geojson.getBounds());
map.addLayer(rectangle);
}
}
function featureGroupBounds() {
if (rectangle) {
rectangle.setBounds(featureGroup.getBounds());
} else {
rectangle = new L.Rectangle(featureGroup.getBounds());
map.addLayer(rectangle);
}
}
</script>
</body>
</html>

Highcharts - How to add background in bar series? And how to print page without lose quality?

I need your help, i try use "pattern" in Highcharts to use background in bars, but the images don't fill the space that i wanna.
Bar Chart Example
I wanna know, how i do to leave the image with -90° than the way that is? And how i do to leave the image with height 100% and width 25%?
And beyond that i wanna of know, how i print the screen without lose the quality in image, because when i press "ctrl+p" i see all all blurred.
Print blurred
Follow below the current code:
<!DOCTYPE html>
<html>
<head>
<title>HighCharts - Pattern Color</title>
<meta http-equiv="Content-Type" contetn="text/html;" charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="http://code.jquery.com/ui/1.9.1/themes/base/jquery-ui.css" />
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="https://code.jquery.com/ui/1.9.1/jquery-ui.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://highcharts.github.io/pattern-fill/pattern-fill.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
</head>
<body>
<script type="text/javascript">
$(document).ready(function() {
Highcharts.chart('container', {
chart: {
type: 'bar',
backgroundColor: null
},
title: {
text: 'Como você está?'
},
xAxis: {
categories: ['']
},
yAxis: {
min: 0,
title: {
text: ''
}
},
legend: {
reversed: true
},
plotOptions: {
series: {
stacking: 'percent',
dataLabels: {
enabled: true,
format: '<b>{point.percentage:.2f}%</b>',
//point.percentage:.1f
color: (Highcharts.theme && Highcharts.theme.dataLabelsColor) || 'white'
}
}
},
series: [{
name: 'Alegre',
data: [30],
color: {
pattern: 'http://emoticons.topfriends.biz/favicon.png',
width: '30',
height: '30'
}
// color: '#B22222'
}, {
name: 'Feliz',
data: [30],
color: {
pattern: 'https://t4.ftcdn.net/jpg/01/58/30/09/240_F_158300957_MhRWEx1vDO6SPVHdGS4dqNG7nLP8rdZ4.jpg',
width: '30',
height: '30'
}
// color: '#2E8B57'
}]
});
});
</script>
<div id="container" style="min-width: 80%; height: 200px; max-width: 80%; margin: 0 auto"></div>
</body>
</html>
Since now i thanks!
1. PATTERN FILL ADJUSTMENT
Refer to this live working example: http://jsfiddle.net/kkulig/opa73k8L/
Full code:
var redrawEnabled = true,
ctr = 0;
$('#container').highcharts({
chart: {
events: {
render: function() {
if (redrawEnabled) {
redrawEnabled = false;
var chart = this,
renderer = chart.renderer;
chart.series[0].points.forEach(function(p) {
var widthRatio = p.shapeArgs.width / p.shapeArgs.height,
id = 'pattern-' + p.index + '-' + ctr;
var pattern = renderer.createElement('pattern').add(renderer.defs).attr({
width: 1,
height: widthRatio,
id: id,
patternContentUnits: 'objectBoundingBox'
});
renderer.image('http://emoticons.topfriends.biz/favicon.png', 0, 0, 1, widthRatio).attr({
}).add(pattern);
p.update({
color: 'url(#' + id + ')'
}, false);
});
ctr++;
chart.redraw();
redrawEnabled = true;
}
}
}
},
series: [{
type: 'bar',
borderWidth: 1,
borderColor: '#000000',
data: [10, 29.9, 71.5]
}]
});
In the redraw event I iterate all the points, create a separate pattern for every point based on its width/height ratio, apply pattern and redraw the chart.
On every redraw new set of patters are created which is not an elegant solution (old ones should be replaced instead). ctr variable is used to create unique name for each pattern.
redrawEnabled serves to prevent infinite recursive loop because chart.redraw() also calls the render event.
In my opinion the easiest way to have the rotated image is to simply provide the already rotated one.
API references:
https://api.highcharts.com/highcharts/chart.events.render
https://api.highcharts.com/class-reference/Highcharts.SVGRenderer#createElement
https://api.highcharts.com/class-reference/Highcharts.SVGRenderer#image
2. PRINTING ISSUE
It seems to be a bug reported here: https://github.com/highcharts/pattern-fill/issues/20

google bubble chart how to add HTML tooltip

I have a Google chart: bubble chart.
I want to add a custom HTML tooltip, with the specified value relative to the point:
<div class="clearfix>
<h3>Metric: []</h3>
<h4>ID comes here: []</h4>
<h4>X Axis Value comes here: []</h4>
<h4>Y Axis Value comes here: []</h4>
<h4>Volume comes here: []</h4>
</div>
Currently it shows a default tooltip, which is not arranged in the way i want. And I cannot edit the fields also.
I want to use Custom HTML tooltip, but sadly it is not supported by Google charts in bubble chart as of yet.
Any way to achieve the same.
MY CODE
JSFIDDLE Demo
<html>
<head>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("visualization", "1", {
packages: ["corechart"]
});
google.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable([
["ID", "X Axis Value", "Y Axis Value", "Metric", "Volume"],
["Range: 2-5", 3, 2.5, "Value Provider", 300],
["Range: 2-5", 4, 2.5, "Third Provider", 239],
["Range: 3-8", 3, 7.4, "Second Provider", 344],
["Range: 5-8", 5, 7.3, "Value Provider", 324],
["Range: 2-10", 9, 2.32, "Third Provider", 765],
["Range: 2-5", 5, 3, "Value Provider", 342],
]);
var options = {
title: 'Range Volume',
hAxis: {
title: 'X Axis'
},
vAxis: {
title: 'Y Axis'
},
bubble: {
textStyle: {
fontSize: 11,
color:'transparent'
}
}
};
var chart = new google.visualization.BubbleChart(document.getElementById('chart_div'));
chart.draw(data, options);
}
</script>
</head>
<body>
<div id="chart_div" style="width: 100%; height: 90vh;"></div>
</body>
Basically you need some kind of mousetracker to know where tooltip should be shown and you need two listeners like this:
google.visualization.events.addListener chart, 'onmouseover', mouseoverHandler
google.visualization.events.addListener chart, 'onmouseout', mouseoutHandler
and you should add id='tooltip' to your tooltip with css like:
#tooltip {
display: none;
position: absolute;
padding: 10px;
border: 1px solid #ddd;
background: white;
width: 350px;
-webkit-box-shadow: 0 0 5px #ddd;
-moz-box-shadow: 0 0 5px #ddd;
box-shadow: 0 0 5px #ddd;
z-index: 1;
}
javascript:
var $tooltip = $('#tooltip')
mouseoverHandler = function(event) {
metric = data.getValue(event.row, 3);
id = data.getValue(event.row, 0);
xAxis = data.getValue(event.row, 1);
yAxis = data.getValue(event.row, 2);
volume = data.getValue(event.row, 4);
$tooltip.find('h3').append(metric);
$tooltip.css({
top: y,
left: x
}).show();
};
mouseoutHandler = function() {
$tooltip.hide();
};
x and y are your mouse cords taken from some kind of mouse tracker like: Javascript - Track mouse position.
title = data.getValue(event.row, 3); is line where you take data from your data from your chart and you have to insert this data into your tooltip the way you want it to be inserted. I hope it will help.

Draw the polyline along the path of the map in openlayer3

I following code how to draw the polyline along path of the map, know for the point present in the map will just give the straight line how to get along the path of the lanes
<!DOCTYPE html>
<html>
<head>
<title>Rotation example</title>
</head>
<body>
<div style="width:80%; height:80%; position:fixed; border: 1px solid;" id="map"></div>
<script src="http://openlayers.org/en/v3.11.2/build/ol.js"></script>
<script>
var lineString = new ol.geom.LineString([
[103.986083, 1.350349],
[103.985097, 1.349067]
]);
lineString.transform('EPSG:4326', 'EPSG:3857');
var lineLayer = new ol.layer.Vector({
source: new ol.source.Vector({
features: [new ol.Feature({
geometry: lineString,
name: 'Line'
})]
}),
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: [255, 255, 0, 0.5],
width: 10
})
})
});
var view = new ol.View({
center: ol.proj.transform([103.986908, 1.353199], 'EPSG:4326','EPSG:3857'),
zoom: 18,
rotation: 68*Math.PI/180
});
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
lineLayer
],
target: 'map',
controls: ol.control.defaults({
attributionOptions: /** #type {olx.control.AttributionOptions} */ ({
collapsible: false
})
}),
view: view
});
</script>
</body>
</html>
Is there any direction service provided by openlayer3 please point to sample to accomplish it
If you have coordinates along the lanes you can use MultiLineString instead of LineString.
You need to use a routing service for such action. You may setup your own service or you may use any online available. here you can find a list of free online routers and decide which one best feet to your needs.
Once you get your root polyline out of the selected service you can then decode (if encoded) and draw your line within your ol3 map.
Also cosider, depending on the service you are going to use, that ol3 offers a class for reading and writing data in the Encoded Polyline Algorithm Format which is described here