0

I've got a React Leaflet map, which renders fine.

I've got a list of plots in state, which appear fine (I can see them if I look at the component state.

Each plot has a GeoJSON polygon property.

I've got a custom marker component, which renders differently based on zoom (either a GeoJSON polygon or a marker at the center of the plot's polygon.)

I'm mapping the plot list and instantiating a custom marker component from each one. But this doesn't render any plots.

What am I missing?

The map component:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../actions';
import { Map, TileLayer, LayersControl, MapControl } from 'react-leaflet';
import { GoogleLayer } from './GoogleLayer';
import { geolocated } from 'react-geolocated';
import 'leaflet-geocoder-mapzen';
import SearchBox from './searchBox';
import Control from 'react-leaflet-control';
import PlotMarker from './plotMarker';
import { centroid } from '@turf/turf';

const { BaseLayer } = LayersControl;
const key = 'MYKEY';
const hybrid = 'HYBRID';
const terrain = 'TERRAIN';
const road = 'ROADMAP';
const satellite = 'SATELLITE';

const centerLat = props => {
    if (
        props.isGeolocationAvailable &&
        props.isGeolocationEnabled &&
        props.coords
    ) {
        return props.coords.latitude;
    }
    return 32.11;
};

const centerLong = props => {
    if (
        props.isGeolocationAvailable &&
        props.isGeolocationEnabled &&
        props.coords
    ) {
        return props.coords.longitude;
    }
    return 34.963;
};

const initialMapCenter = props => {
    return [centerLat(props), centerLong(props)];
};

const initialZoomLevel = 11;

const markers = props => {
    if (props.plots) {
        return (
            <div>
                {(props.filteredPlots || props.plots).map(
                    plot =>
                        plot.feature && (
                            <PlotMarker
                                key={plot._id}
                                geoJSON={plot.feature}
                                position={centroid(plot.feature).geometry.coordinates}
                            />
                        )
                )}
            </div>
        );
    }
};
export class PlotMap extends Component {
    render() {
        this.props.plots &&
            (this.props.filteredPlots || this.props.plots).forEach(plot => {
                plot.feature &&
                    console.log(centroid(plot.feature).geometry.coordinates);
            });
        return (
            <div
                className="col-sm-8 m-auto p-0 flex-column float-right"
                style={{ height: `85vh` }}>
                <Map
                    center={initialMapCenter(this.props)}
                    zoom={initialZoomLevel}
                    zoomControl={true}
                    onZoomend={e => {
                        this.props.setZoomLevel(e.target.getZoom());
                    }}
                    onMoveEnd={e => {
                        this.props.setMapCenter(e.target.getCenter());
                    }}>
                    <LayersControl position="topright">
                        <BaseLayer name="Google Maps Roads">
                            <GoogleLayer googlekey={key} maptype={road} />
                        </BaseLayer>
                        <BaseLayer name="Google Maps Terrain">
                            <GoogleLayer googlekey={key} maptype={terrain} />
                        </BaseLayer>
                        <BaseLayer name="Google Maps Satellite">
                            <GoogleLayer googlekey={key} maptype={satellite} />
                        </BaseLayer>
                        <BaseLayer checked name="Google Maps Hybrid">
                            <GoogleLayer
                                googlekey={key}
                                maptype={hybrid}
                                libraries={['geometry', 'places']}
                            />
                        </BaseLayer>
                    </LayersControl>
                    <SearchBox postion="bottomright" />
                    {markers(this.props)}
                </Map>
            </div>
        );
    }
}

function mapStateToProps(state) {
    return {
        filteredPlots: state.plots.filteredPlots,
        plots: state.plots.plots,
        mapCenter: state.plots.mapCenter
    };
}

export default geolocated({
    positionOptions: {
        enableHighAccuracy: false
    },
    userDecisionTimeout: 5000,
    suppressLocationOnMount: false
})(connect(mapStateToProps, actions)(PlotMap));

The marker component:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../actions';
import { Marker, GeoJSON } from 'react-leaflet';

export class PlotMarker extends Component {
    render() {
        const { key, position, geoJSON, zoomLevel } = this.props;
        if (zoomLevel > 14) {
            return <GeoJSON key={key} data={geoJSON} />;
        }
        return <Marker key={key} position={position} />;
    }
}

function mapStateToProps(state) {
    return {
        selectedPlot: state.plots.selectedPlot,
        plotBeingEdited: state.plots.plotBeingEdited,
        zoomLevel: state.plots.zoomLevel
    };
}

export default connect(mapStateToProps, actions)(PlotMarker);

1 Answer 1

1

The issue turns out to be that GeoJSON uses long-lat, while Leaflet uses lat-long (inherited from Google Maps.) So my markers were appearing in another part of the world. Fixing this is very easy-just call .reverse() on the array of coordinates you're passing into the Marker component as position, like this:

<PlotMarker
      key={plot._id}
      geoJSON={plot.feature}
      position={centroid(plot.feature).geometry.coordinates}
/>
1
  • I don't see any reverse() function being called?
    – Matthias
    Commented Sep 25, 2023 at 15:23

Not the answer you're looking for? Browse other questions tagged or ask your own question.