Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
538 views
in Technique[技术] by (71.8m points)

How to extend TileLayer component in react-leaflet v3?

I am trying to extend the TileLayer component in 'react-leaflet' v3. It is necessary to override this function to provide custom tile URL naming scheme. An example of what I need, written in basic leaflet:

function initMap() {
    L.TileLayer.WebGis = L.TileLayer.extend({

        initialize: function (url, options) {
            options = L.setOptions(this, options);
            if (options.detectRetina && L.Browser.retina && options.maxZoom > 0) {
                options.tileSize = Math.floor(options.tileSize / 2);
                options.zoomOffset++;
                if (options.minZoom > 0) {
                    options.minZoom--;
                }
                this.options.maxZoom--;
            }
            if (options.bounds) {
                options.bounds = L.latLngBounds(options.bounds);
            }
            this._url = url + "/gis_render/{x}_{y}_{z}/" + options.userId + "/tile.png";
            var subdomains = this.options.subdomains;
            if (typeof subdomains === 'string') {
                this.options.subdomains = subdomains.split('');
            }
        },

        getTileUrl: function (tilePoint) {
            return L.Util.template(this._url, L.extend({
                s: this._getSubdomain(tilePoint),
                z: 17 - this._map._zoom,
                x: tilePoint.x,
                y: tilePoint.y
            }, this.options));
        }
    });

    L.tileLayer.webGis = function (url, options) {
        return new L.TileLayer.WebGis(url, options);
    };

    // create a map in the "map" div, set the view to a given place and zoom
    var map = L.map('map').setView([53.9, 27.55], 10);

    // add an Gurtam Maps tile layer
    L.tileLayer.webGis(wialon.core.Session.getInstance().getBaseGisUrl('render'), {
        attribution: 'Gurtam Maps',
        minZoom: 4,
        userId: wialon.core.Session.getInstance().getCurrUser().getId()
    }).addTo(map);

}

If I just write a url of Gurtam maps to a 'url' prop of TileLayer component, then my map incorrectly displayed (zoom and tile errors).

I can't figure out what to use for the correct display:

  1. Use 'useRef' hook to get the current TileLayer instance and extend it.
  2. Use some hook (maybe createElementHook) from package 'react-leaflet/core' and create my own custom component
  3. Or something else

I would be grateful for any explanations.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

It sounds like you're trying to create a custom component in react-leaflet v3. If you're not super familiar with react-leaflet, this might be daunting. The docs on writing a custom component are a little hard to follow. I found this section helpful: Element hook factory

So you need to start with 2 basic functions. One function to create your element, and one to update it. To create it,

// Assuming you've already defined L.TileLayer.WebGis somewhere
// and attached it to the global L

const createWebGisLayer = (props, context) => {

  const instance = L.tileLayer.webGis(props.url, {...props})

  return { instance, context }

}

Then you need another function that will handle any updates you want to trickle down into your component. For example, if a certain prop of your component changes, you need to explicitly tell react-leaflet to update the underlying leaflet instance of that component:

const updateWebGisLayer = (instance, props, prevProps) => {

  if (prevProps.url !== props.url) {
    if (instance.setUrl) instance.setUrl(props.url)
  }

  if (prevProps.userId !== props.userId) {
    if (instance.setUserId) instance.setUserId(props.userId)
  }

}

You need these setter functions to tell react-leaflet that if the url or userId (or whatever) prop changes in react, it needs to rerender the leaflet layer. setUrl already exists on L.TileLayer, but you'll need to define a setUserId that updated the userId option of the L.TileLayer.WebGis instance. If you don't include these, your component will not update when its props changes.

To put it all together, you can use the createLayerComponent factory function:

const WebGisLayer = createLayerComponent(createWebGisLayer, updateWebGisLayer)
export WebGisLayer

WebGisLayer is now a react component that you can use as a child of a MapContainer

const App = () => {

  const [userId, setUserId] = useState('user123')

  return (
    <MapContainer center={center} zoom={zoom}>
      <WebGisLayer 
        url="some_url" 
        userId={userId}
        otherPropsYouNeed={otherProps} />
    </MapContainer>
  )

}

When the component loads it will run your leaflet code and add the leaflet component to the map. If the userId changes due to some setstate call, your updateWebGisLayer function tells react-leaflet to update the underlying leaflet component.

There's a lot of ways to do this, but this is the one I think is most straightforward. I haven't had a chance to test this code so you'll inevitably have to play around with it to get it working, but this should get you started.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...