How do I add custom image to mapbox marker? - vue.js

const marker = document.createElement('div');
marker.innerHTML = `<img src="../../assets/FuelPin.png" style="height: 56px; width: 44px" alt=""/>`;
new mapboxgl.Marker(marker)
.setLngLat(lngLatPopup)
.addTo(this.map);
},
In the above code the src from img attribute is not loading the image. I tried using :src to bind as vue component but it is being rendered as string. My relative path to the image exists as well.

In general changing innerHTML with JavaScript is not something frameworks like Vue or React will let you do (see https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML#security_considerations)
Here is the plain JS example from the Mapbox GL website:
const el = document.createElement('div');
const width = marker.properties.iconSize[0];
const height = marker.properties.iconSize[1];
el.className = 'marker';
el.style.backgroundImage = `url(https://placekitten.com/g/${width}/${height}/)`;
el.style.width = `${width}px`;
el.style.height = `${height}px`;
el.style.backgroundSize = '100%';
el.addEventListener('click', () => {
window.alert(marker.properties.message);
});
// Add markers to the map.
new mapboxgl.Marker(el)
.setLngLat(marker.geometry.coordinates)
.addTo(map);
From https://docs.mapbox.com/mapbox-gl-js/example/custom-marker-icons/
One pro tip, if you have lots of images to load (like thousands of markers) you will get better performance if you have MapboxGL render it in WebGL vs using the DOM, for that see: https://docs.mapbox.com/mapbox-gl-js/example/add-image/

Related

Vue Leaflet Setting custom TileLayer bounds

I'm trying to display a large number of images using leaflet and I'm having problems making the tileLayers display properly.
I'm extending the TileLayer class and overriding the createTile function like this:
`
let imageLayer = TileLayer.extend({
createTile: function (coords, done) {
const image = window.L.DomUtil.create("img");
image.src = `http://placekitten.com/g/200/300`;
image.crossOrigin = "";
image.onload = function () {
this.onload = function () {};
done(null, image);
};
return image;
}
});
Result
I want to change the position of the images and to only display them in areas I have already defined (for example between offsetting the image at 0,0 to be at 1,1 or using latLng
([8273.300000000001, 10618.02] , [9232.653535353536, 9658.666464646465])).
Ive tried a bunch of different methods and almost always came back to bounds, which don't work and setting the bounds doesn't change anything.
I don't want to use ImageOverlays because the images I'm trying to display are sliced and named with the leaflet naming scheme (0_0_1.png).

Vue: get image width + height returns zero on component load

My component is a modal which displays an <img> with some tags on top.
I get the image dimensions using const size = this.$refs.media.getBoundingClientRect().
However, the height and width is 0, so the tags become placed wrong.
So it seems the image hasn't loaded yet? If I delay getting the height+width with setTimeout, I do get the width and height. But it seems like a not too reliable workaround.
I have "v-cloak" on the modal, but that doesn't work.
I can get the html element with this.$refs.media, but it doesn't mean that the image is loaded, apparently.
So, is there a way to check if the image is loaded? Is there some way to delay the component until the image is loaded?
One more thing: if I add a "width" and "height" in inline style on the img, then those values are available directly. But I don't want to set the size that way because it ruins the responsiveness.
The code doesn't say much more, but it is basically:
<modal>
<vue-draggable-resizable // <-- each tag
v-for="( tag, index ) in tags"
:x="convertX(tag.position.x)"
:y="convertY(tag.position.y)"
(other parameters)
convertX (x) {
const size = this.$refs.media.getBoundingClientRect()
return x * (size.width - 100) // <--- 0 width
async mounted () {
this.renderTags(this.media.tags) // <-- needs img width and height
Updated, solution
As the below answer says, I can use the onload event. I also came across the "#load" event, which I tried and it worked. I can't find it in the documentation though(?) (I searched for #load and v-on:load). Using "onload" directly on the image element didn't work though, so #load seems to be the way.
If I have a data property:
data () {
return {
imgLoaded: false
I can set it to true with a method using #load on the image:
<img
#load="whenImgLoaded"
<template v-if="imgLoaded = true">
<vue-draggable-resizable
:x="convertX(tag.position.x)"
:y="convertY(tag.position.y)"
(other parameters)
methods: {
whenImgLoaded(){
this.imgLoaded = true;
this.renderTags(this.media.tags)
}
You could load the image first via javascript, and after it's loaded run the renderTags method.
E.g., do something like this:
async mounted() {
let image = new Image();
image.onload = function () {
this.renderTags(this.media.tags)
};
image.src = 'https://some-image.png'
}

Cannot fit bounds in mapbox-gl , in Vue app

I am using vue 2.6.10 and I have the following dependencies vue-mapbox 0.4.1 and mapbox-gl 1.3.2
The map works, but I cannot get all the points of the map, create bounds and make the map zoom there.
What I am doing now, based on this and this
In my template
<MglMap
ref="map"
#load="mapLoaded()">
and then in my javascript
import Mapbox from "mapbox-gl";
components: { Mapbox}
methods :{
mapLoaded(){
let coords = [];
//coords is array , contains arrays like [-118.578, 51.524]
let bounds = coords.reduce((bounds, coord)=> {
return bounds.extend(coord);
}, this.mapbox.LngLatBounds(coords[0], coords[0])
);
this.$refs['map'].map.fitBounds(bounds, { padding: 20 });
}
},
created(){
this.mapbox = Mapbox;
}
This should work, but I keep getting
TypeError: this.setSouthWest is not a function
so I guess there is a problem in this.mapbox.LngLatBounds(coords[0], coords[0]) , maybe the this.mapbox. or the LngLatBounds does not work.
If I console log the coords[0], prints an array [10.467778, 37.600833]
What am I missing? Thanks
I have got the same error. Turns out I have missed "new" keyword.
You can try adding the "new" keyword for "this.mapbox.LngLatBounds"
The following code worked for me.
import mapboxgl from 'mapbox-gl';
Vue.prototype.$maps = mapboxgl
this.map = new this.$maps.Map({container: 'mapContainer', // container ID
style: 'mapbox://styles/mapbox/streets-v11', // style URL
zoom: 9 // starting zoom
});
const sourceMarker = new this.$maps.Marker().setLngLat(origin).addTo(this.map);
const destMarker = new this.$maps.Marker().setLngLat(destination).addTo(this.map);
const bounds = new this.$maps.LngLatBounds(origin, destination);
this.map.fitBounds(bounds);

Unable to download React Canvas Drawing

I want to download my drawing on the React canvas as a jpeg image file to my desktop, and then pass it to a python file for classification. Can someone specify a code to download the React canvas drawing, or suggest a better way to implement the idea?
clearCanvas({nativeEvent}) {
var image = this.canvas.toDataURL("image/jpeg");
nativeEvent.href=image;
this.ctx.save("C:/Users/PrishitaRay/Pictures/Myimage.jpeg");
this.ctx = this.canvas.getContext('2d');
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
This function just clears the react canvas without downloading it first.
After reading your code, I'm not sure if its related to React. Therefore I'm going to give a non-react solution.
In case you have done some drawing on the canvas like my exampleDrawing function, then just call download function like this.
function exampleDrawing(){
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0,0,150,75);
}
function download(){
var canvas = document.getElementById("canvas");
var url = canvas.toDataURL("image/png");
var link = document.createElement('a');
link.download = 'filename.png';
link.href = url;
link.click();
}
exampleDrawing();
<canvas id="canvas" width="200" height="100">
</canvas>
<button onclick="download();">Download!</button>
What my download function do is to create a URL that contains the canvas's image, generate a link, and then click on it.
And of course you don't have to generate a button and click on it. Just call the download function whenever you want.
Download Canvas as Image in React (Js only): (React Class Components)
Download As Image Function: chartRef.canvas gives the canvas element, replace it with your Canvas element.
handleChartDownload = (chartRef) => {
const chartCanvas = chartRef.canvas;
if (chartCanvas) {
const url = chartCanvas.toDataURL("image/png");
const link = document.createElement("a");
link.download = "chart.png";
link.href = url;
link.click();
}
};
Graph Element: these elements comes under render() function.
Download Button:
<button
className="btn btn-light"
onClick={() => this.handleChartDownload(this.chartRef)}
>Download</button>
Graph: (here Scatter is ChartJsReact component, used for creating graphs)
<Scatter
ref={(reference) => (this.chartRef = reference)}
data={this.state.chartData}
options={this.state.chartOptions}
/>

Google map ignoring marker size for retina icons

I have a Google map driven by Advanced Custom Fields. I have created an iconType to dictate which icon is used as the marker depending on the custom field.
My marker images are 80px square but I want to force them to 40px square for retina reasons. I have tried lots of variations of code I've found via google etc but nothing works and the size gets ignored showing my markers at the larger 80px. My guess is because I have the 2 icons depending on iconType?
Can anyone help me force the size of these markers to 40px square please...
function add_marker( $marker, map ) {
// var
var latlng = new google.maps.LatLng( $marker.attr('data-lat'), $marker.attr('data-lng'));
var iconType = $marker.attr('data-icon');
var iconBase = '<?php echo get_template_directory_uri(); ?>/assets/';
var size = new google.maps.Size(40, 40);
if (iconType === 'Costa') {
iconName = 'costa#2x.png';
} else {
iconName = 'starbucks#2x.png';
}
// create marker
var marker = new google.maps.Marker({
position : latlng,
map : map,
clickable: false,
animation: google.maps.Animation.DROP,
icon: iconBase + iconName,
});
// add to array
map.markers.push( marker );
// if marker contains HTML, add it to an infoWindow
if( $marker.html() )
{
// create info window
var infowindow = new google.maps.InfoWindow({
content : $marker.html()
});
}
}
I did something like this recently, and I believe you'll need to use the new Icon object to specify your custom Icon properties, and use the size and scaledSize properties. I fixed my issues following this guide:
How to add retina ready pin marker to your Google maps?