I tried to follow the react-leaflet custom component documentation which requires to extend one of the existing react wrapper classes. I have hard time to apply it to a Non-map base layers plugin which is rendering a plain image leaflet-rastercoords.
I appreciate if you can provide some starting points for this simple plug-in, which I assume should update the <Map /> props somehow.
Many thanks,
Mike
You are not required to extend one of the base classes, it can be a simple React component, but the base classes provide some common logic that are usually required to support Leaflet elements.
If the Leaflet plugin you want to use does not render or provide anything to its children, it's unlikely it makes sense as a component, you're probably better using it directly.
This plugin does render an image using the map. My question is if I can extend the <Map /> and customize its view the same way.
Here is the plugin code:
;(function (factory) {
var L
if (typeof define === 'function' && define.amd) {
// AMD
define(['leaflet'], factory)
} else if (typeof module !== 'undefined') {
// Node/CommonJS
L = require('leaflet')
module.exports = factory(L)
} else {
// Browser globals
if (typeof window.L === 'undefined') {
throw new Error('Leaflet must be loaded first')
}
factory(window.L)
}
}(function (L) {
/**
* L.RasterCoords
* @param {L.map} map - the map used
* @param {Array} imgsize - [ width, height ] image dimensions
* @param {Number} [tilesize] - tilesize in pixels. Default=256
*/
L.RasterCoords = function (map, imgsize, tilesize) {
this.map = map
this.width = imgsize[0]
this.height = imgsize[1]
this.tilesize = tilesize || 256
this.zoom = this.zoomLevel()
if (this.width && this.height) {
this.setMaxBounds()
}
}
L.RasterCoords.prototype = {
/**
* calculate accurate zoom level for the given image size
*/
zoomLevel: function () {
return Math.ceil(
Math.log(
Math.max(this.width, this.height) /
this.tilesize
) / Math.log(2)
)
},
/**
* unproject `coords` to the raster coordinates used by the raster image projection
* @param {Array} coords - [ x, y ]
* @return {L.LatLng} - internal coordinates
*/
unproject: function (coords) {
return this.map.unproject(coords, this.zoom)
},
/**
* project `coords` back to image coordinates
* @param {Array} coords - [ x, y ]
* @return {L.LatLng} - image coordinates
*/
project: function (coords) {
return this.map.project(coords, this.zoom)
},
/**
* sets the max bounds on map
*/
setMaxBounds: function () {
var southWest = this.unproject([0, this.height])
var northEast = this.unproject([this.width, 0])
this.map.setMaxBounds(new L.LatLngBounds(southWest, northEast))
}
}
return L.RasterCoords
}))
and here is how to use it:
var img = [
3831, // original width of image `karta.jpg`
3101 // original height of image
]
// create the map
var map = L.map(mapid, {
minZoom: minZoom,
maxZoom: maxZoom
})
// assign map and image dimensions
var rc = new L.RasterCoords(map, img)
// set the view on a marker ...
map.setView(rc.unproject([1589, 1447]), 4)
// the tile layer containing the image generated
L.tileLayer('./tiles/{z}/{x}/{y}.png', {
noWrap: true,
attribution: 'xyz'
}).addTo(map)
Would be great to know the best approach to incorporate this plugin.
Yes you can extend Map or anything else you want.
Can you please let me know how I can overwrite the map's view only?
class MyMap extends Map {... and implement the lifecycle handlers you need, check the code and documentation.
Where should I override the setview method for Map component? Should it be in componentDidMount()? The problem is by then the map has been constructed already!
I don't know, it's up to whatever you want to implement.
any update on this? @PaulLeCam no offense, but your answers aren't helpful at all :( please give some guidance on how we can use that plugin with your library.
Working example based on code from: https://commenthol.github.io/leaflet-rastercoords/
Made that example shorter to keep it concise. Enjoy.
import React from 'react'
import { Map, TileLayer, Marker, Popup } from 'react-leaflet'
import Leaflet from 'leaflet'
import RasterCoords from 'leaflet-rastercoords'
class MapExtended extends Map {
createLeafletElement(props) {
let LeafletMapElement = super.createLeafletElement(props);
let img = [
3831, // original width of image `karta.jpg`
3101 // original height of image
]
// assign map and image dimensions
let rc = new RasterCoords(LeafletMapElement, img)
// set the view on a marker ...
LeafletMapElement.setView(rc.unproject([1589, 1447]), 4)
// the tile layer containing the image generated with gdal2tiles --leaflet ...
// Leaflet.tileLayer('https://commenthol.github.io/leaflet-rastercoords/example/tiles/{z}/{x}/{y}.png', {
// noWrap: true,
// attribution: 'Map <a href="https://commons.wikimedia.org/wiki/' +
// 'File:Karta_%C3%B6ver_Europa,_1672_-_Skoklosters_slott_-_95177.tif">' +
// 'Karta 枚ver Europa, 1672 - Skoklosters</a> under ' +
// '<a href="https://creativecommons.org/publicdomain/zero/1.0/deed.en">CC0</a>'
// }).addTo(LeafletMapElement)
return LeafletMapElement;
}
}
export default class LeafletMap extends React.Component {
render() {
return (
<MapExtended
minZoom={0}
maxZoom={5}
style={{width: "1000px", height: "1000px"}}>
{/* the tile layer containing the image generated with gdal2tiles --leaflet ... */}
<TileLayer
url="https://commenthol.github.io/leaflet-rastercoords/example/tiles/{z}/{x}/{y}.png"
noWrap={true}
attribution={'Map <a href="https://commons.wikimedia.org/wiki/' +
'File:Karta_%C3%B6ver_Europa,_1672_-_Skoklosters_slott_-_95177.tif">' +
'Karta 枚ver Europa, 1672 - Skoklosters</a> under ' +
'<a href="https://creativecommons.org/publicdomain/zero/1.0/deed.en">CC0</a>'}
/>
</MapExtended>
)
}
}
Don't forget about createLeafletElement props parameter if you need access to props.
If you try to use TileLayer from 'react-leaflet' on map, it will overwrite Leaflet.tileLayer from createLeafletElement. So watch out on that or just declare TileLayer in MapExtended directly.
Thanks @mzmrk, I was having a really difficult time figuring this out. Your example worked for me!
Hello, I know this is a long-shot on an old thread @PaulLeCam, but I am trying to implement this example from @mzmrk on the current version of react-leaflet and getting a pretty useless Cannot call a class as a function error from react 馃槥 .
I've looked through the docs but am pretty unfamiliar with extending Leaflet or React classes so was wondering if it was a simple fix to update with the new concepts newer versions of the library have brought? I've tried updating the Map component to MapContainer (below) but that's as far as I've got.
If not I will continue to try and find a solution, if I get anywhere I will post it here for anyone looking in future.
import React from "react";
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";
import Leaflet from "leaflet";
import RasterCoords from "leaflet-rastercoords";
class MapExtended extends MapContainer {
createLeafletElement(props) {
let LeafletMapElement = super.createLeafletElement(props);
let img = [
3831, // original width of image `karta.jpg`
3101, // original height of image
];
// assign map and image dimensions
let rc = new RasterCoords(LeafletMapElement, img);
// set the view on a marker ...
LeafletMapElement.setView(rc.unproject([1589, 1447]), 4);
return LeafletMapElement;
}
}
export default class BaseMap extends React.Component {
render() {
return (
<MapExtended
minZoom={0}
maxZoom={5}
style={{ width: "1000px", height: "1000px" }}
>
{/* the tile layer containing the image generated with gdal2tiles --leaflet ... */}
<TileLayer
url="https://commenthol.github.io/leaflet-rastercoords/example/tiles/{z}/{x}/{y}.png"
noWrap={true}
attribution={
'Map <a href="https://commons.wikimedia.org/wiki/' +
'File:Karta_%C3%B6ver_Europa,_1672_-_Skoklosters_slott_-_95177.tif">' +
"Karta 枚ver Europa, 1672 - Skoklosters</a> under " +
'<a href="https://creativecommons.org/publicdomain/zero/1.0/deed.en">CC0</a>'
}
/>
</MapExtended>
);
}
}
Most helpful comment
Working example based on code from: https://commenthol.github.io/leaflet-rastercoords/
Made that example shorter to keep it concise. Enjoy.
Don't forget about
createLeafletElementprops parameter if you need access to props.If you try to use
TileLayerfrom 'react-leaflet' on map, it will overwriteLeaflet.tileLayerfromcreateLeafletElement. So watch out on that or just declare TileLayer in MapExtended directly.