openlayers icon indicating copy to clipboard operation
openlayers copied to clipboard

How to remove borders of other countries and display only India on the map?

Open prernasyan opened this issue 10 months ago • 3 comments

Hi OpenLayers team,

I'm working on a React project using OpenLayers and trying to restrict the map view to India only. I have India-specific layers like states, districts, and subdistricts (in TopoJSON format), which work well. However, the base layer (Google Maps via XYZ tiles) still shows borders of other countries, which I would like to hide or mask.

Objective:

I want to hide all international country borders except for India (i.e., prevent borders or features of neighboring countries from being visible). The goal is to keep the context strictly within India for the users.

Current Setup:

I'm using multiple layers, including:

  • Google Maps base layers via XYZ tile source
  • India-specific vector layers using TopoJSON for states/districts/subdistricts
  • Magnify overlay for interactive zoom

Here's my code

import React, { useEffect, useRef } from 'react';
import 'ol/ol.css';
import 'ol-ext/dist/ol-ext.css';

const MapComponent = ({ onLocationSelect }) => {
  const mapRef = useRef(null);
  const magnifyRef = useRef(null);

  useEffect(() => {
    // Dynamically import OpenLayers modules to ensure they only run in browser environment
    import('ol/Map').then(({ default: Map }) => {
      import('ol/View').then(({ default: View }) => {
        import('ol/layer/Tile').then(({ default: TileLayer }) => {
          import('ol/source/XYZ').then(({ default: XYZ }) => {
            import('ol/proj').then(({ fromLonLat, toLonLat }) => {
              import('ol-ext/control/LayerSwitcher').then(
                ({ default: LayerSwitcher }) => {
                  import('ol-ext/overlay/Magnify').then(
                    ({ default: Magnify }) => {
                      import('ol/layer/Vector').then(
                        ({ default: VectorLayer }) => {
                          import('ol/source/Vector').then(
                            ({ default: VectorSource }) => {
                              import('ol/format/GeoJSON').then(
                                ({ default: GeoJSON }) => {
                                  import('ol/style/Style').then(
                                    ({ default: Style }) => {
                                      import('ol/style/Stroke').then(
                                        ({ default: Stroke }) => {
                                          import('ol/style/Fill').then(
                                            ({ default: Fill }) => {
                                              import(
                                                'ol/format/TopoJSON'
                                              ).then(
                                                ({ default: TopoJSON }) => {
                                                  // Create options objects separately from layer creation to support properties like 'title'
                                                  // that aren't in the TypeScript definitions

                                                  // Google Maps base layer
                                                  const googleMapsOptions = {
                                                    source: new XYZ({
                                                      url: 'https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',
                                                      attributions:
                                                        '© Google Maps',
                                                      maxZoom: 20,
                                                    }),
                                                  };
                                                  // Use type assertion to add the title property
                                                  const googleMapsLayer =
                                                    new TileLayer(
                                                      googleMapsOptions
                                                    );
                                                  // Add title as a property to the layer object
                                                  googleMapsLayer.set(
                                                    'title',
                                                    'Google Maps'
                                                  );

                                                  // Google Satellite layer
                                                  const googleSatelliteOptions =
                                                    {
                                                      source: new XYZ({
                                                        url: 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
                                                        attributions:
                                                          '© Google Maps',
                                                        maxZoom: 20,
                                                      }),
                                                      visible: false,
                                                    };
                                                  const googleSatelliteLayer =
                                                    new TileLayer(
                                                      googleSatelliteOptions
                                                    );
                                                  googleSatelliteLayer.set(
                                                    'title',
                                                    'Google Satellite'
                                                  );

                                                  // Google Hybrid layer
                                                  const googleHybridOptions = {
                                                    source: new XYZ({
                                                      url: 'https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}',
                                                      attributions:
                                                        '© Google Maps',
                                                      maxZoom: 20,
                                                    }),
                                                    visible: false,
                                                  };
                                                  const googleHybridLayer =
                                                    new TileLayer(
                                                      googleHybridOptions
                                                    );
                                                  googleHybridLayer.set(
                                                    'title',
                                                    'Google Hybrid'
                                                  );

                                                  // India States Vector Layer
                                                  const indiaStatesSource =
                                                    new VectorSource({
                                                      url: '/topoJsons/india_states.json',
                                                      format: new TopoJSON(),
                                                    });

                                                  const indiaStatesLayer =
                                                    new VectorLayer({
                                                      source:
                                                        indiaStatesSource,
                                                      style: new Style({
                                                        stroke: new Stroke({
                                                          color: '#3388ff',
                                                          width: 1,
                                                        }),
                                                        fill: new Fill({
                                                          color:
                                                            'rgba(255, 255, 255, 0.1)',
                                                        }),
                                                      }),
                                                      //   visible: false,
                                                    });
                                                  indiaStatesLayer.set(
                                                    'title',
                                                    'India States'
                                                  );

                                                  // India Districts Vector Layer
                                                  const indiaDistrictSource =
                                                    new VectorSource({
                                                      url: '/topoJsons/india_districts.json',
                                                      format: new TopoJSON(),
                                                    });

                                                  const indiaDistrictsLayer =
                                                    new VectorLayer({
                                                      source:
                                                        indiaDistrictSource,
                                                      style: new Style({
                                                        stroke: new Stroke({
                                                          color: '#FF0000',
                                                          width: 1,
                                                        }),
                                                        fill: new Fill({
                                                          color:
                                                            'rgba(255, 255, 255, 0.1)',
                                                        }),
                                                      }),
                                                      visible: false,
                                                    });
                                                  indiaDistrictsLayer.set(
                                                    'title',
                                                    'India Districts'
                                                  );

                                                  // India Subdistricts Vector Layer
                                                  const indiaSubdistrictSource =
                                                    new VectorSource({
                                                      url: '/topoJsons/india_subdistricts.json',
                                                      format: new TopoJSON(),
                                                    });

                                                  const indiaSubdistrictsLayer =
                                                    new VectorLayer({
                                                      source:
                                                        indiaSubdistrictSource,
                                                      style: new Style({
                                                        stroke: new Stroke({
                                                          color: '#FFFF00',
                                                          width: 1,
                                                        }),
                                                        fill: new Fill({
                                                          color:
                                                            'rgba(255, 255, 255, 0.1)',
                                                        }),
                                                      }),
                                                      visible: false,
                                                    });
                                                  indiaSubdistrictsLayer.set(
                                                    'title',
                                                    'India Subdistricts'
                                                  );

                                                  const map = new Map({
                                                    target: mapRef.current,
                                                    layers: [
                                                      googleMapsLayer,
                                                      googleSatelliteLayer,
                                                      googleHybridLayer,
                                                      indiaStatesLayer,
                                                      indiaDistrictsLayer,
                                                      indiaSubdistrictsLayer,
                                                    ],
                                                    view: new View({
                                                      // Adjusted center to precisely cover India
                                                      center: fromLonLat([
                                                        80.9, // Longitude adjusted to center India
                                                        22.5, // Latitude adjusted to center India
                                                      ]),
                                                      zoom: 4, // Reduced zoom to show full country

                                                      // Uncomment and adjust the extent to restrict the view to India
                                                      extent: fromLonLat([
                                                        31.0, // Western longitude (slightly adjusted for full coverage)
                                                        5.5, // Southern latitude (slightly adjusted for full coverage)
                                                      ]).concat(
                                                        fromLonLat([
                                                          107.5, // Eastern longitude (slightly adjusted for full coverage)
                                                          57.5, // Northern latitude (adjusted to include entire northern region)
                                                        ])
                                                      ),

                                                      // Optional: Add min and max zoom constraints
                                                      minZoom: 4,
                                                      maxZoom: 8,
                                                    }),
                                                  });

                                                  const switcher =
                                                    new LayerSwitcher();
                                                  map.addControl(switcher);

                                                  // Magnify overlay
                                                  const magnifySource =
                                                    new XYZ({
                                                      url: 'https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',
                                                      attributions:
                                                        '© Google Maps',
                                                      maxZoom: 20,
                                                    });

                                                  const magnifyLayer =
                                                    new TileLayer({
                                                      source: magnifySource,
                                                    });

                                                  const magnify = new Magnify({
                                                    layers: [magnifyLayer],
                                                    radius: 120,
                                                    zoomOffset: 1,
                                                    rotateWithView: false,
                                                    stabilized: true,
                                                  });

                                                  map.addOverlay(magnify);
                                                  magnifyRef.current = magnify;

                                                  map.on(
                                                    'pointermove',
                                                    (evt) => {
                                                      magnify.setPosition(
                                                        evt.coordinate
                                                      );
                                                    }
                                                  );

                                                  map.on('click', (evt) => {
                                                    const lonLat = toLonLat(
                                                      evt.coordinate
                                                    );
                                                    // Pass the click coordinates to the parent component
                                                    onLocationSelect({
                                                      lat: lonLat[1],
                                                      lon: lonLat[0],
                                                    });
                                                  });

                                                  setTimeout(() => {
                                                    map.updateSize();
                                                  }, 100);
                                                }
                                              );
                                            }
                                          );
                                        }
                                      );
                                    }
                                  );
                                }
                              );
                            }
                          );
                        }
                      );
                    }
                  );
                }
              );
            });
          });
        });
      });
    });

    return () => {
      // Cleanup function to prevent memory leaks
      if (mapRef.current) {
        mapRef.current.innerHTML = '';
      }
    };
  }, []);

  return (
    <div
      ref={mapRef}
      className="map-container"
      style={{
        width: '100%',
        margin: 'auto',
        height: '750px',
        border: '1px solid #ccc',
      }}
    />
  );
};

export default MapComponent;

Any help or example would be much appreciated!

Thanks for this amazing library and all your support.

prernasyan avatar May 20 '25 09:05 prernasyan

hi

  • so if i understand in fact you want a google ressource with satellite and label but without the administrative boundary from goggle ? (so satellite is ok but label (hybride) have also the admin ...) i have never experimented this and dont know but i think that google have not a ressource xyz for this ??

jipexu avatar May 22 '25 11:05 jipexu

I believe this example is what you are looking for: Vector Clipping Layer. Basically, you just need to provide a vector layer that is the boundary for India and on the post render of the tiles you will applying the India vector layer boundary. In the examples case they have an extra layer under the layer they are clipping so you can ignore adding that extra detail.

MannyKSoSo avatar May 22 '25 12:05 MannyKSoSo

I’m currently seeing the map like this:

Image

I want to display proper map labels, similar to the view below. Currently, the vector layer only adds the Indian states using a topojson file (india_states.json) that contains just the state boundaries — no labels or additional data.

Here’s the relevant code from my MapComponent:

import React, { useEffect, useRef } from 'react';
import 'ol/ol.css';
import 'ol-ext/dist/ol-ext.css';

const MapComponent = ({ onLocationSelect }) => {
  const mapRef = useRef(null);
  const magnifyRef = useRef(null);

  useEffect(() => {
    // Dynamically import OpenLayers modules to ensure they only run in browser environment
    import('ol/Map').then(({ default: Map }) => {
      import('ol/View').then(({ default: View }) => {
        import('ol/layer/Tile').then(({ default: TileLayer }) => {
          import('ol/source/OSM').then(({ default: OSM }) => {
            import('ol/proj').then(({ fromLonLat, toLonLat }) => {
              import('ol-ext/control/LayerSwitcher').then(
                ({ default: LayerSwitcher }) => {
                  import('ol-ext/overlay/Magnify').then(
                    ({ default: Magnify }) => {
                      import('ol/layer/Vector').then(
                        ({ default: VectorLayer }) => {
                          import('ol/source/Vector').then(
                            ({ default: VectorSource }) => {
                              import('ol/format/GeoJSON').then(
                                ({ default: GeoJSON }) => {
                                  import('ol/style/Style').then(
                                    ({ default: Style }) => {
                                      import('ol/style/Stroke').then(
                                        ({ default: Stroke }) => {
                                          import('ol/style/Fill').then(
                                            ({ default: Fill }) => {
                                              import(
                                                'ol/format/TopoJSON'
                                              ).then(
                                                ({ default: TopoJSON }) => {
                                                  import('ol/render').then(
                                                    ({ getVectorContext }) => {
                                                      // OSM Base layer
                                                      const osmLayer =
                                                        new TileLayer({
                                                          source: new OSM(),
                                                        });

                                                      // India States Vector Layer (used for clipping)
                                                      const indiaStatesSource =
                                                        new VectorSource({
                                                          url: '/topoJsons/india_states.json',
                                                          format:
                                                            new TopoJSON(),
                                                        });

                                                      const indiaStatesLayer =
                                                        new VectorLayer({
                                                          source:
                                                            indiaStatesSource,
                                                          style: new Style({
                                                            stroke: new Stroke(
                                                              {
                                                                color:
                                                                  '#3388ff',
                                                                width: 2,
                                                              }
                                                            ),
                                                            fill: new Fill({
                                                              color:
                                                                'rgba(51, 136, 255, 0.1)',
                                                            }),
                                                          }),
                                                        });

                                                      // Set extent for clipping when features are loaded
                                                      indiaStatesSource.on(
                                                        'addfeature',
                                                        function () {
                                                          const extent =
                                                            indiaStatesSource.getExtent();
                                                          osmLayer.setExtent(
                                                            extent
                                                          );
                                                        }
                                                      );

                                                      // Clipping style for rendering
                                                      const clipStyle =
                                                        new Style({
                                                          fill: new Fill({
                                                            color: 'black',
                                                          }),
                                                        });

                                                      // Add post-render event for clipping
                                                      osmLayer.on(
                                                        'postrender',
                                                        function (e) {
                                                          const vectorContext =
                                                            getVectorContext(
                                                              e
                                                            );
                                                          e.context.globalCompositeOperation =
                                                            'destination-in';
                                                          indiaStatesSource.forEachFeature(
                                                            function (
                                                              feature
                                                            ) {
                                                              vectorContext.drawFeature(
                                                                feature,
                                                                clipStyle
                                                              );
                                                            }
                                                          );
                                                          e.context.globalCompositeOperation =
                                                            'source-over';
                                                        }
                                                      );

                                                      // India Districts Vector Layer
                                                      const indiaDistrictSource =
                                                        new VectorSource({
                                                          url: '/topoJsons/india_districts.json',
                                                          format:
                                                            new TopoJSON(),
                                                        });

                                                      const indiaDistrictsLayer =
                                                        new VectorLayer({
                                                          source:
                                                            indiaDistrictSource,
                                                          style: new Style({
                                                            stroke: new Stroke(
                                                              {
                                                                color:
                                                                  '#FF0000',
                                                                width: 1,
                                                              }
                                                            ),
                                                            fill: new Fill({
                                                              color:
                                                                'rgba(255, 0, 0, 0.1)',
                                                            }),
                                                          }),
                                                          visible: false,
                                                        });
                                                      indiaDistrictsLayer.set(
                                                        'title',
                                                        'India Districts'
                                                      );

                                                      // India Subdistricts Vector Layer
                                                      const indiaSubdistrictSource =
                                                        new VectorSource({
                                                          url: '/topoJsons/india_subdistricts.json',
                                                          format:
                                                            new TopoJSON(),
                                                        });

                                                      const indiaSubdistrictsLayer =
                                                        new VectorLayer({
                                                          source:
                                                            indiaSubdistrictSource,
                                                          style: new Style({
                                                            stroke: new Stroke(
                                                              {
                                                                color:
                                                                  '#FFFF00',
                                                                width: 1,
                                                              }
                                                            ),
                                                            fill: new Fill({
                                                              color:
                                                                'rgba(255, 255, 0, 0.1)',
                                                            }),
                                                          }),
                                                          visible: false,
                                                        });
                                                      indiaSubdistrictsLayer.set(
                                                        'title',
                                                        'India Subdistricts'
                                                      );

                                                      const map = new Map({
                                                        target: mapRef.current,
                                                        layers: [
                                                          osmLayer,
                                                          indiaStatesLayer,
                                                          indiaDistrictsLayer,
                                                          indiaSubdistrictsLayer,
                                                        ],
                                                        view: new View({
                                                          center: fromLonLat([
                                                            78.9629, 20.5937,
                                                          ]), // Center of India
                                                          zoom: 4.5,
                                                          // Remove extent restriction to show full India
                                                          minZoom: 4,
                                                          maxZoom: 10,
                                                        }),
                                                      });

                                                      const switcher =
                                                        new LayerSwitcher();
                                                      map.addControl(switcher);

                                                      // Magnify overlay with OSM source
                                                      const magnifySource =
                                                        new OSM();
                                                      const magnifyLayer =
                                                        new TileLayer({
                                                          source:
                                                            magnifySource,
                                                        });

                                                      const magnify =
                                                        new Magnify({
                                                          layers: [
                                                            magnifyLayer,
                                                          ],
                                                          radius: 120,
                                                          zoomOffset: 2,
                                                          rotateWithView:
                                                            false,
                                                          stabilized: true,
                                                        });

                                                      map.addOverlay(magnify);
                                                      magnifyRef.current =
                                                        magnify;

                                                      map.on(
                                                        'pointermove',
                                                        (evt) => {
                                                          magnify.setPosition(
                                                            evt.coordinate
                                                          );
                                                        }
                                                      );

                                                      map.on(
                                                        'click',
                                                        (evt) => {
                                                          const lonLat =
                                                            toLonLat(
                                                              evt.coordinate
                                                            );
                                                          // Pass the click coordinates to the parent component
                                                          onLocationSelect({
                                                            lat: lonLat[1],
                                                            lon: lonLat[0],
                                                          });
                                                        }
                                                      );

                                                      setTimeout(() => {
                                                        map.updateSize();
                                                      }, 100);
                                                    }
                                                  );
                                                }
                                              );
                                            }
                                          );
                                        }
                                      );
                                    }
                                  );
                                }
                              );
                            }
                          );
                        }
                      );
                    }
                  );
                }
              );
            });
          });
        });
      });
    });

    return () => {
      // Cleanup function to prevent memory leaks
      if (mapRef.current) {
        mapRef.current.innerHTML = '';
      }
    };
  }, [onLocationSelect]);

  return (
    <div
      ref={mapRef}
      className="map-container"
      style={{
        width: '100%',
        margin: 'auto',
        height: '750px',
        border: '1px solid #ccc',
      }}
    />
  );
};

export default MapComponent;

I want my map to look more like this, with detailed base map labels visible:

Image

Is there a way to retain OSM (or a similar base map) with proper labeling while still applying the clipping/masking based on my TopoJSON boundaries?

Any help would be appreciated!

prernasyan avatar May 23 '25 11:05 prernasyan