Carol.Kar
Carol.Kar

Reputation: 5355

Color certain grid tiles based on coordinates

I would like to color certain grid tiles based on their coordinates.

I created the following grid:

<!DOCTYPE html>
<html>

<head>
    <title>Color Tiles</title>
    <meta charset="utf-8" />
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />

    <style>
        body {
            padding: 0;
            margin: 0;
        }

        html,
        body,
        #map {
            height: 100%;
            width: 100%;
        }
    </style>
</head>

<body>

  <div id="map"></div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
    <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>

    <!-- Make sure you put this AFtER leaflet.js, when using with leaflet 
    <script src="https://unpkg.com/[email protected]/dist/geosearch.umd.js"></script>
    -->

    <script>
        var map = new L.Map('map', { center: [10, 0], zoom: 2 });

        let zoomLevel = map.getZoom()
        console.log("zoomLevel " + zoomLevel)

        // if(zoomLevel === 1) {
            var tiles = new L.GridLayer();
            tiles.createTile = function (coords) {
                var tile = L.DomUtil.create('canvas', 'leaflet-tile');
                var ctx = tile.getContext('2d');
                var size = this.getTileSize()
                // console.log("size: " + size.toString())
                tile.width = size.x
                tile.height = size.y

                // calculate projection coordinates of top left tile pixel
                var nwPoint = coords.scaleBy(size)

                // calculate geographic coordinates of top left tile pixel
                var nw = map.unproject(nwPoint, coords.z)

                ctx.fillStyle = 'white';
                ctx.fillRect(0, 0, size.x, 50);
                ctx.fillStyle = 'black';
                ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
                ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
                ctx.strokeStyle = 'black';
                ctx.beginPath();
                ctx.moveTo(0, 0);
                ctx.lineTo(size.x - 1, 0);
                ctx.lineTo(size.x - 1, size.y - 1);
                ctx.lineTo(0, size.y - 1);
                ctx.closePath();
                ctx.stroke();
                return tile;
            }

            L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                attribution: 'Map data &copy; <a href="http://www.osm.org">OpenStreetMap</a>',
            }).addTo(map)

            tiles.addTo(map)
    
    </script>
</body>

</html>

I would like to have the following:

enter image description here

I tried to style it directly via css, which seems to work. However, I would like to style it based on the provided coordinates.

Any suggestion how to do this?

Upvotes: 1

Views: 1385

Answers (4)

Steve
Steve

Reputation: 10886

What type of Coordinates?

Looking at your other question about using maps for a turn-based game, there may be some ambiguity over what "coordinates" means. You may mean geographic coordinates (i.e. latitude and longitude) which represents a point on a map or globe. On the other hand, you may mean coordinates to a tile (e.g. the 4th row, 5th column, at zoom level 3 of a certain tileset) which would represent an area on a map or globe. I'll go over approaches for both cases.

Geographic Coordinates

Leaflet has built in ways to detect whether specific LatLng coordinates are within bounds, by using the LatLngBounds.contains method.

First you can define an array of key coordinates and then create leaflet LatLng objects from them:

var keyCoordinates = [
  [ -5.9, -123.9 ],
  [ -12, -28.5 ]
];
var leafletKeyCoordinates = keyCoordinates.map(function (c) {
  return new L.LatLng(c[0], c[1]);
});

To determine the boundaries of a specific tile, you can calculate the southwest corner of the tile. From this part:

// calculate projection coordinates of top left tile pixel
var nwPoint = coords.scaleBy(size)
// calculate geographic coordinates of top left tile pixel
var nw = map.unproject(nwPoint, coords.z);

Add:

// calculate projection coordinates of bottom right tile pixel
var sePoint = nwPoint.add(size);
// calculate geographic coordinates of bottom right tile pixel
var se = map.unproject(sePoint, coords.z);
var bounds = new L.LatLngBounds(nw, se);

From there, you can use the bounds for each tile to determine whether it should be shaded. You can do this by either drawing on the canvas or applying a class to the tile. For an example using the canvas, at the bottom of createTile, add:

// if it contains any coordinates, it should be filled
var shouldShade = leafletKeyCoordinates.some(function (a) {
  return bounds.contains(a);
});
if (shouldShade) {
  ctx.fillStyle = 'rgba(0, 0, 240, 0.3)';
  ctx.fillRect(0, 0, size.x, size.y);
}

All Together:

<!DOCTYPE html>
<html>

<head>
    <title>Color Tiles</title>
    <meta charset="utf-8" />
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />

    <style>
        body {
            padding: 0;
            margin: 0;
        }

        html,
        body,
        #map {
            height: 100%;
            width: 100%;
        }
    </style>
</head>

<body>

  <div id="map"></div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
    <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>

    <!-- Make sure you put this AFtER leaflet.js, when using with leaflet 
    <script src="https://unpkg.com/[email protected]/dist/geosearch.umd.js"></script>
    -->

    <script>
        var map = new L.Map('map', { center: [10, 0], zoom: 2 });

        let zoomLevel = map.getZoom()
        console.log("zoomLevel " + zoomLevel)

        var keyCoordinates = [
          [ -5.9, -123.9 ],
          [ -12, -28.5 ]
        ];
        var leafletKeyCoordinates = keyCoordinates.map(function (c) {
          return new L.LatLng(c[0], c[1]);
        });

        // if(zoomLevel === 1) {
            var tiles = new L.GridLayer();
            tiles.createTile = function (coords) {
                var tile = L.DomUtil.create('canvas', 'leaflet-tile');
                var ctx = tile.getContext('2d');
                var size = this.getTileSize()
                // console.log("size: " + size.toString())
                tile.width = size.x
                tile.height = size.y

                // calculate projection coordinates of top left tile pixel
                var nwPoint = coords.scaleBy(size)

                // calculate geographic coordinates of top left tile pixel
                var nw = map.unproject(nwPoint, coords.z)
                // calculate projection coordinates of bottom right tile pixel
                var sePoint = nwPoint.add(size);
                // calculate geographic coordinates of bottom right tile pixel
                var se = map.unproject(sePoint, coords.z);
                var bounds = new L.LatLngBounds(nw, se);

                ctx.fillStyle = 'white';
                ctx.fillRect(0, 0, size.x, 50);
                ctx.fillStyle = 'black';
                ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
                ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
                ctx.strokeStyle = 'black';
                ctx.beginPath();
                ctx.moveTo(0, 0);
                ctx.lineTo(size.x - 1, 0);
                ctx.lineTo(size.x - 1, size.y - 1);
                ctx.lineTo(0, size.y - 1);
                ctx.closePath();
                ctx.stroke();

                // if it contains any coordinates, it should be filled
                var shouldShade = leafletKeyCoordinates.some(function (a) {
                  return bounds.contains(a);
                });
                if (shouldShade) {
                  ctx.fillStyle = 'rgba(0, 0, 240, 0.3)';
                  ctx.fillRect(0, 0, size.x, size.y);
                }
return tile;
            }

            L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                attribution: 'Map data &copy; <a href="http://www.osm.org">OpenStreetMap</a>',
            }).addTo(map)

            tiles.addTo(map)
    
    </script>
</body>

</html>

Tile Coordinates

In the case of tiles, you can directly look at coords.x and coords.y for the column and row, and coords.z for the zoom level. The tricky part of this is that if there are varying levels of zoom, your user may be at a different zoom than the tiles you've identified.

var tileCoordinates = [
  [0, 3, 3], // x, y, and zoom of tile
  [28, 6, 5]
];

To translate between zoom levels, each zoom level doubles the tile coordinates (for example, the tile (0, 2) at zoom level 4 is represented by the tiles (0, 4), (0, 5), (1, 4), (1, 5) at zoom level 5)). We can use Math.pow(2, targetZoom - coords.z) or Math.pow(2, coords.z - targetZoom) to determine the factor to multiply by.

var { x, y, z } = coords;
var shouldShade = tileCoordinates.some(function (c) {
  let [ tx, ty, tz] = c, multiple, sx, sy;
  if (z < tz) {
     // map is zoomed out from the target
     // you may just want to `return false;` here
     multiple = Math.pow(2, tz - z);
     sx = Math.floor(tx / multiple);
     sy = Math.floor(ty / multiple);
     return sx === x && sy === y; 
  } else if (z > tz) {
     // map is zoomed in from the target
     multiple = Math.pow(2, z - tz);
     sx = Math.floor(x / multiple);
     sy = Math.floor(y / multiple);
     return sx === tx && sy === ty; 
  }
  return tx === x && ty === y;
});

All together:

<!DOCTYPE html>
<html>

<head>
    <title>Color Tiles</title>
    <meta charset="utf-8" />
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />

    <style>
        body {
            padding: 0;
            margin: 0;
        }

        html,
        body,
        #map {
            height: 100%;
            width: 100%;
        }
    </style>
</head>

<body>

  <div id="map"></div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
    <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>

    <!-- Make sure you put this AFtER leaflet.js, when using with leaflet 
    <script src="https://unpkg.com/[email protected]/dist/geosearch.umd.js"></script>
    -->

    <script>
        var map = new L.Map('map', { center: [10, 0], zoom: 2 });

        let zoomLevel = map.getZoom()
        console.log("zoomLevel " + zoomLevel)

        var tileCoordinates = [
          [0, 3, 3],
          [28, 6, 5]
        ];

        // if(zoomLevel === 1) {
            var tiles = new L.GridLayer();
            tiles.createTile = function (coords) {
                var tile = L.DomUtil.create('canvas', 'leaflet-tile');
                var ctx = tile.getContext('2d');
                var size = this.getTileSize()
                // console.log("size: " + size.toString())
                tile.width = size.x
                tile.height = size.y

                // calculate projection coordinates of top left tile pixel
                var nwPoint = coords.scaleBy(size)

                // calculate geographic coordinates of top left tile pixel
                var nw = map.unproject(nwPoint, coords.z)
                // calculate projection coordinates of bottom right tile pixel
                var sePoint = nwPoint.add(size);
                // calculate geographic coordinates of bottom right tile pixel
                var se = map.unproject(sePoint, coords.z);
                var bounds = new L.LatLngBounds(nw, se);

                ctx.fillStyle = 'white';
                ctx.fillRect(0, 0, size.x, 50);
                ctx.fillStyle = 'black';
                ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
                ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
                ctx.strokeStyle = 'black';
                ctx.beginPath();
                ctx.moveTo(0, 0);
                ctx.lineTo(size.x - 1, 0);
                ctx.lineTo(size.x - 1, size.y - 1);
                ctx.lineTo(0, size.y - 1);
                ctx.closePath();
                ctx.stroke();

                var { x, y, z } = coords;
                var shouldShade = tileCoordinates.some(function (c) {
                  let [ tx, ty, tz] = c, multiple, sx, sy;
                  if (z < tz) {
                    // map is zoomed out from the target
                    // you may just want to `return false;` here
                    multiple = Math.pow(2, tz - z);
                    sx = Math.floor(tx / multiple);
                    sy = Math.floor(ty / multiple);
                    return sx === x && sy === y; 
                  } else if (z > tz) {
                    // map is zoomed in from the target
                    multiple = Math.pow(2, z - tz);
                    sx = Math.floor(x / multiple);
                    sy = Math.floor(y / multiple);
                    return sx === tx && sy === ty; 
                  }
                  return tx === x && ty === y;
                });

                if (shouldShade) {
                  ctx.fillStyle = 'rgba(0, 0, 240, 0.3)';
                  ctx.fillRect(0, 0, size.x, size.y);
                }
return tile;
            }

            L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                attribution: 'Map data &copy; <a href="http://www.osm.org">OpenStreetMap</a>',
            }).addTo(map)

            tiles.addTo(map)
    
    </script>
</body>

</html>

Upvotes: 2

Tordanik
Tordanik

Reputation: 1353

This snippet simultaneously solves the related question. It decides which tiles to color based on an array of TileNumber objects:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>GridLayer Test</title>
    <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
    <style>
        body {
            padding: 0;
            margin: 0;
        }

        html,
        body,
        #map {
            height: 100%;
            width: 100%;
        }
    </style>
</head>

<body>
<div id="map"></div>

<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>

<script>

    const numTilesX = 2 ** 17
    const numTilesY = 2 ** 17

    class TileNumber {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
        equals(other) {
            return this.x === other.x && this.y === other.y;
        }
    }

    let coloredTiles = [
        new TileNumber(70435, 45249),
        new TileNumber(70434, 45248),
        new TileNumber(70441, 45245)
    ]

    function latLngToTileNumber(latLng) {
        const lngDegrees = latLng.lng;
        const latRadians = latLng.lat * (Math.PI/180);
        return new L.Point(
            numTilesX * ((lngDegrees + 180) / 360),
            numTilesY * (1 - Math.log(Math.tan(latRadians) + 1 / Math.cos(latRadians)) / Math.PI) / 2
        );
    }

    const map = new L.Map('map', {center: [48.5748229, 13.4609744], zoom: 16, maxZoom: 19});

    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: 'Map data &copy; <a href="https://www.osm.org">OpenStreetMap</a>', maxZoom: 19
    }).addTo(map)

    const tiles = new L.GridLayer({minZoom: 12});
    tiles.createTile = function (coords) {
        const tile = L.DomUtil.create('canvas', 'leaflet-tile');
        const ctx = tile.getContext('2d');
        const size = this.getTileSize();
        tile.width = size.x
        tile.height = size.y

        // calculate projection coordinates of top left tile pixel
        const nwPoint = coords.scaleBy(size);
        // calculate geographic coordinates of top left tile pixel
        const nw = map.unproject(nwPoint, coords.z);
        // calculate fraction tile number at top left point
        const nwTile = latLngToTileNumber(nw, Math.floor)

        // calculate projection coordinates of bottom right tile pixel
        const sePoint = new L.Point(nwPoint.x + size.x - 1, nwPoint.y + size.y - 1)
        // calculate geographic coordinates of bottom right tile pixel
        const se = map.unproject(sePoint, coords.z);
        // calculate fractional tile number at bottom right point
        const seTile = latLngToTileNumber(se, Math.ceil)

        const minTileX = nwTile.x
        const maxTileX = seTile.x
        const minTileY = nwTile.y
        const maxTileY = seTile.y

        for (let x = Math.ceil(minTileX) - 1; x <= Math.floor(maxTileX) + 1; x++) {
            for (let y = Math.ceil(minTileY) - 1; y <= Math.floor(maxTileY) + 1; y++) {

                let tile = new TileNumber(x, y)

                const xMinPixel = Math.round(size.x * (x - minTileX) / (maxTileX - minTileX));
                const xMaxPixel = Math.round(size.x * (x + 1 - minTileX) / (maxTileX - minTileX));
                const yMinPixel = Math.round(size.y * (y - minTileY) / (maxTileY - minTileY));
                const yMaxPixel = Math.round(size.y * (y + 1 - minTileY) / (maxTileY - minTileY));

                // fill the rectangle with a color
                ctx.fillStyle = coloredTiles.some(t => t.equals(tile))
                    ? 'rgba(0, 0, 255, 0.3)'
                    : 'rgba(255, 255, 255, 0)';
                ctx.fillRect(xMinPixel, yMinPixel, xMaxPixel - xMinPixel, yMaxPixel - yMinPixel);

                if (coords.z >= 16) {
                    // draw the white rectangle and text at the top of the cell
                    ctx.fillStyle = 'white';
                    ctx.fillRect(xMinPixel, yMinPixel, xMaxPixel - xMinPixel, 28);
                    ctx.fillStyle = 'black';
                    ctx.font = "15px Arial"
                    ctx.fillText(tile.x + "," + tile.y, xMinPixel + 10, yMinPixel + 20, xMaxPixel - xMinPixel);
                }

                if (coords.z >= 13) {
                    // draw a border
                    ctx.strokeStyle = 'black';
                    ctx.strokeRect(xMinPixel, yMinPixel, xMaxPixel - xMinPixel, yMaxPixel - yMinPixel);
                }

            }
        }

        return tile;

    }

    tiles.addTo(map);

    map.on('click', e => {

        const fractionalTileNumber = latLngToTileNumber(e.latlng);
        const tileNumber = new TileNumber(Math.floor(fractionalTileNumber.x), Math.floor(fractionalTileNumber.y));

        console.log("Tile " + tileNumber.x + " " + tileNumber.y  + " clicked");

        if (coloredTiles.some(t => t.equals(tileNumber))) {
            coloredTiles = coloredTiles.filter(t => !t.equals(tileNumber));
        } else {
            coloredTiles.push(tileNumber);
        }

        tiles.redraw();

    });

</script>
</body>
</html>

Upvotes: 1

Falke Design
Falke Design

Reputation: 11338

You can use the internal _tileCoordsToBounds function from Leaflet to get the bounds of the tile and check if the latlng is in it.

I extended L.GridLayer because it is much cleaner. The important function is _isLatLngInCoords:

L.ColoredTiles = L.GridLayer.extend({
    colorLatLngs: [],
    initialize (latlngs, options) {
        L.Util.setOptions(this, options);
        if(latlngs && L.Util.isArray(latlngs) && latlngs.length > 0) {
            this.colorLatLngs = latlngs;
        }

        if(this.options.debug) {
            this._showDebugLatLngs();
        }
    },
    createTile (coords) {
        var tile = L.DomUtil.create('canvas', 'leaflet-tile');
        var ctx = tile.getContext('2d');
        var size = this.getTileSize()
        // console.log("size: " + size.toString())
        tile.width = size.x
        tile.height = size.y

        // calculate projection coordinates of top left tile pixel
        var nwPoint = coords.scaleBy(size)

        // calculate geographic coordinates of top left tile pixel
        var nw = map.unproject(nwPoint, coords.z)

        ctx.fillStyle = 'white';
        ctx.fillRect(0, 0, size.x, 50);
        ctx.fillStyle = 'black';
        ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
        ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
        ctx.strokeStyle = 'black';
        ctx.beginPath();
        ctx.moveTo(0, 0);
        ctx.lineTo(size.x - 1, 0);
        ctx.lineTo(size.x - 1, size.y - 1);
        ctx.lineTo(0, size.y - 1);
        ctx.closePath();
        ctx.stroke();

        if (this._isLatLngInCoords(coords)) {
            ctx.fillStyle = 'rgba(0, 0, 240, 0.3)';
            ctx.fillRect(0, 0, size.x, size.y);
        }

        return tile;
    },
    _isLatLngInCoords(coords) {
        var tileBounds = this._tileCoordsToBounds(coords);
        return this.colorLatLngs && this.colorLatLngs.some(function (a) {
            return tileBounds.contains(a);
        });
    },
    setColorLatLngs(latlngs){
        this.colorLatLngs = latlngs;
        this.redraw();

        if(this.options.debug) {
            this._showDebugLatLngs();
        }
    },
    _showDebugLatLngs(){
        this.fg = this.fg || L.featureGroup().addTo(map);
        this.fg.clearLayers();
        this.colorLatLngs && this.colorLatLngs.forEach((latlng)=>{
            L.marker(latlng).addTo(this.fg);
        })
    }
});

And then add it to the map:

var colorLatLngs = [
    L.latLng(0,0),
    L.latLng(2,2),
];
        
var tiles = new L.ColoredTiles(colorLatLngs).addTo(map)

I also added the function setColorLatLngs(array) to change the latlngs and update the colored tiles.

tiles.setColorLatLngs([L.latLng(3,3)]);

And for the debug purpose I added the option debug: true to set markers on each latlng. So it is clearly visible which tiles should be colored.

var tiles = new L.ColoredTiles(colorLatLngs, {debug: true}).addTo(map)

Demo:

<!DOCTYPE html>
<html>

<head>
<title>Color Tiles</title>
<meta charset="utf-8" />
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />

<style>
    body {
        padding: 0;
        margin: 0;
    }

    html,
    body,
    #map {
        height: 100%;
        width: 100%;
    }
</style>
</head>

<body>

<div id="map"></div>

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>

<!-- Make sure you put this AFtER leaflet.js, when using with leaflet 
<script src="https://unpkg.com/[email protected]/dist/geosearch.umd.js"></script>
-->

<script>
var map = new L.Map('map', { center: [10, 0], zoom: 2 });

let zoomLevel = map.getZoom()
console.log("zoomLevel " + zoomLevel)

L.ColoredTiles = L.GridLayer.extend({
    colorLatLngs: [],
    initialize (latlngs, options) {
        L.Util.setOptions(this, options);
        if(latlngs && L.Util.isArray(latlngs) && latlngs.length > 0) {
            this.colorLatLngs = latlngs;
        }

        if(this.options.debug) {
            this._showDebugLatLngs();
        }
    },
    createTile (coords) {
        var tile = L.DomUtil.create('canvas', 'leaflet-tile');
        var ctx = tile.getContext('2d');
        var size = this.getTileSize()
        // console.log("size: " + size.toString())
        tile.width = size.x
        tile.height = size.y

        // calculate projection coordinates of top left tile pixel
        var nwPoint = coords.scaleBy(size)

        // calculate geographic coordinates of top left tile pixel
        var nw = map.unproject(nwPoint, coords.z)

        ctx.fillStyle = 'white';
        ctx.fillRect(0, 0, size.x, 50);
        ctx.fillStyle = 'black';
        ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
        ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
        ctx.strokeStyle = 'black';
        ctx.beginPath();
        ctx.moveTo(0, 0);
        ctx.lineTo(size.x - 1, 0);
        ctx.lineTo(size.x - 1, size.y - 1);
        ctx.lineTo(0, size.y - 1);
        ctx.closePath();
        ctx.stroke();

        if (this._isLatLngInCoords(coords)) {
            ctx.fillStyle = 'rgba(0, 0, 240, 0.3)';
            ctx.fillRect(0, 0, size.x, size.y);
        }

        return tile;
    },
    _isLatLngInCoords(coords) {
        var tileBounds = this._tileCoordsToBounds(coords);
        return this.colorLatLngs && this.colorLatLngs.some(function (a) {
            return tileBounds.contains(a);
        });
    },
    setColorLatLngs(latlngs){
        this.colorLatLngs = latlngs;
        this.redraw();

        if(this.options.debug) {
            this._showDebugLatLngs();
        }
    },
    _showDebugLatLngs(){
        this.fg = this.fg || L.featureGroup().addTo(map);
        this.fg.clearLayers();
        this.colorLatLngs && this.colorLatLngs.forEach((latlng)=>{
            L.marker(latlng).addTo(this.fg);
        })
    }
});

L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: 'Map data &copy; <a href="http://www.osm.org">OpenStreetMap</a>',
}).addTo(map)

var colorLatLngs = [
    L.latLng(0,0),
    L.latLng(2,2),
];
var tiles = new L.ColoredTiles(colorLatLngs, {debug: true}).addTo(map)
</script>
</body>

</html>

Upvotes: 2

Mteuahasan
Mteuahasan

Reputation: 520

I'm thinking about something this way :

Added :

if (nw.lat > 10) {
   ctx.fillStyle = "rgba(0, 0, 0, 0.4)";
   ctx.fillRect(0, 50, size.x, size.y - 50);
}

Up to you to use any condition that allow you to localize the tiles.

<!DOCTYPE html>
<html>

<head>
    <title>Color Tiles</title>
    <meta charset="utf-8" />
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />

    <style>
        body {
            padding: 0;
            margin: 0;
        }

        html,
        body,
        #map {
            height: 100%;
            width: 100%;
        }
    </style>
</head>

<body>

  <div id="map"></div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
    <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>

    <!-- Make sure you put this AFtER leaflet.js, when using with leaflet 
    <script src="https://unpkg.com/[email protected]/dist/geosearch.umd.js"></script>
    -->

    <script>
        var map = new L.Map('map', { center: [10, 0], zoom: 2 });

        let zoomLevel = map.getZoom()
        console.log("zoomLevel " + zoomLevel)

        // if(zoomLevel === 1) {
            var tiles = new L.GridLayer();
            tiles.createTile = function (coords) {
                var tile = L.DomUtil.create('canvas', 'leaflet-tile');
                var ctx = tile.getContext('2d');
                var size = this.getTileSize()
                // console.log("size: " + size.toString())
                tile.width = size.x
                tile.height = size.y

                // calculate projection coordinates of top left tile pixel
                var nwPoint = coords.scaleBy(size)

                // calculate geographic coordinates of top left tile pixel
                var nw = map.unproject(nwPoint, coords.z)

                ctx.fillStyle = 'white';
                ctx.fillRect(0, 0, size.x, 50);
                if (nw.lat > 10) {
                    ctx.fillStyle = "rgba(0, 0, 0, 0.4)";
                    ctx.fillRect(0, 50, size.x, size.y - 50);
                }
                ctx.fillStyle = 'black';
                ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
                ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
                ctx.strokeStyle = 'black';
                ctx.beginPath();
                ctx.moveTo(0, 0);
                ctx.lineTo(size.x - 1, 0);
                ctx.lineTo(size.x - 1, size.y - 1);
                ctx.lineTo(0, size.y - 1);
                ctx.closePath();
                ctx.stroke();
                return tile;
            }

            L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                attribution: 'Map data &copy; <a href="http://www.osm.org">OpenStreetMap</a>',
            }).addTo(map)

            tiles.addTo(map)
    
    </script>
</body>

</html>

Upvotes: 2

Related Questions