Last active
April 9, 2025 05:07
-
-
Save josephrocca/b4d0a13b0e679f85506d46c33c5481f5 to your computer and use it in GitHub Desktop.
Generate map tiles from a large image with nodejs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Given a large input image, this script generates map tiles of 256*256px | |
// at power-of-2 zoom levels. It's not super efficient or anything, just a | |
// quickly hacked together script. | |
//Use the tiles in leaflet.js like this: | |
/* | |
<div id='map' style='height:100%;'></div> | |
<script> | |
var map = L.map('map', { | |
attributionControl: false | |
}).setView([0, 0], 1); | |
var layer_url = './images/{z}-{x}-{y}.png'; | |
L.tileLayer(layer_url, { | |
maxZoom: 20, // <-- change to suit | |
noWrap: true | |
}).addTo(map); | |
</script> | |
*/ | |
// Instructions: | |
// 1. create a new directory | |
// 2. run `npm init -y` | |
// 3. run `npm install sharp --save` | |
// 4. copy your image into the directory and call it `input.png` | |
// 5. make a folder called `output` | |
// 6. run this script with `node generate.js` | |
(async function() { | |
let input = 'input.png'; // <-- this is your input image | |
let sharp = require('sharp'); | |
let metadata = await sharp(input).metadata(); | |
let fullPixelSize = Math.max(metadata.width, metadata.height); | |
await sharp(input) | |
.resize(fullPixelSize, fullPixelSize) | |
.background({r: 0, g: 0, b: 0, alpha: 0}) | |
.embed() | |
.toFile("squarified.png"); | |
console.log("squarified ✓"); | |
//sharp.cache(false); | |
let zoom = 0; | |
while(true) { | |
// double size for each consecutive zoom level, starting at 256px: | |
let pixelSize = 256 * 2**zoom; | |
if(pixelSize > fullPixelSize) { | |
zoom--; | |
break; | |
} | |
console.log(`Processing zoom level ${zoom}...`); | |
await sharp("squarified.png") | |
.resize(pixelSize, pixelSize) | |
.toFile(`resized-${zoom}.png`); | |
let rows = 2**zoom; | |
let cols = 2**zoom; | |
for(let row = 0; row < rows; row++) { | |
for(let col = 0; col < cols; col++) { | |
await sharp(`resized-${zoom}.png`) | |
.extract({ left: col*256, top: row*256, width: 256, height: 256 }) | |
.toFile(`./output/${zoom}-${col}-${row}.png`); | |
} | |
} | |
zoom++; | |
} | |
if(zoom < 0) { | |
console.error(`Seems that you've input an image that's less than 256 pixels`); | |
} else { | |
console.log(`Finished at zoom=${zoom}`); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for this script! One note: on L39 I got a "TypeError: sharp(...).resize(...).background is not a function" error.
Poking around, looks like sharp v0.34.1 requires a different way to call (lovell/sharp#1392). This worked for me: