Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ Common options:
* `-b` or `--bounds`: Bounding box in WSEN format, comma separated (required)
* `-Z` or `--maxzoom`: Maximum zoom level (required)
* `-z` or `--minzoom`: Minimum zoom level (optional, 0 if not provided)
* `-r` or `--ratio`: Output pixel ratio (optional, 1 if not provided)
* `-t` or `--tiletype`: Output tile type (jpg, png, or webp) (optional, jpg if not provided)
* `-o` or `--outputdir`: Output directory (optional, "outputs/" if not provided)
* `-f` or `--filename`: Name of the output MBTiles file (optional, "output" if not provided)

Expand Down
20 changes: 20 additions & 0 deletions src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ program
"(Required) Maximum zoom level",
parseInt,
)
.option("-r, --ratio <number>", "Map pixel ratio (default 1)", Math.floor, 1)
.option(
"-o, --outputdir <type>",
"Output directory (default 'outputs/')",
Expand All @@ -61,6 +62,17 @@ program
"-f, --filename <type>",
"Output filename (default 'output')",
"output",
)
.option(
"-t, --tiletype <type>",
"Tile type (jpg, png, or webp)",
(type) => {
if (!["jpg", "png", "webp"].includes(type)) {
throw new Error("Invalid tile type");
}
return type;
},
"jpg",
);

program.parse(process.argv);
Expand All @@ -76,10 +88,12 @@ const {
openstreetmap: openStreetMap,
overlay,
bounds,
ratio,
minzoom: minZoom,
maxzoom: maxZoom,
outputdir: outputDir,
filename: outputFilename,
tiletype,
} = options;

validateInputOptions(
Expand All @@ -92,8 +106,10 @@ validateInputOptions(
openStreetMap,
overlay,
bounds,
ratio,
minZoom,
maxZoom,
tiletype,
);

console.log("\n\n-------- Rendering map tiles with Maplibre GL --------");
Expand All @@ -111,8 +127,10 @@ if (overlay) console.log("Overlay: %j", overlay);
console.log("Bounding box: %j", bounds);
console.log("Min zoom: %j", minZoom);
console.log("Max zoom: %j", maxZoom);
console.log("Ratio: %j", ratio);
console.log("Output directory: %j", outputDir);
console.log("Output MBTiles filename: %j", outputFilename);
console.log("Output Tile Type: %j", tiletype);
console.log("------------------------------------------------------");

const renderResult = await initiateRendering(
Expand All @@ -125,10 +143,12 @@ const renderResult = await initiateRendering(
openStreetMap,
overlay,
bounds,
ratio,
minZoom,
maxZoom,
outputDir,
outputFilename,
tiletype,
);

// output the render result to console
Expand Down
42 changes: 17 additions & 25 deletions src/generate_resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,36 +66,24 @@ export const generateStyle = (
};

// Convert premultiplied image buffer from Mapbox GL to RGBA PNG format
export const generateJPG = async (buffer, width, height, ratio) => {
// Un-premultiply pixel values
// Mapbox GL buffer contains premultiplied values, which are not handled
// correctly by sharp https://github.com/mapbox/mapbox-gl-native/issues/9124
// since we are dealing with 8-bit RGBA values, normalize alpha onto 0-255
// scale and divide it out of RGB values

for (let i = 0; i < buffer.length; i += 4) {
const alpha = buffer[i + 3];
const norm = alpha / 255;
if (alpha === 0) {
buffer[i] = 0;
buffer[i + 1] = 0;
buffer[i + 2] = 0;
} else {
buffer[i] /= norm;
buffer[i + 1] = buffer[i + 1] / norm;
buffer[i + 2] = buffer[i + 2] / norm;
}
}

return sharp(buffer, {
export const generateImage = async (buffer, tiletype, width, height, ratio) => {
const image = sharp(buffer, {
raw: {
premultiplied: true,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

premultiplied was added in sharp 0.28.2 :-) lovell/sharp#1599 (comment)

So no need to Un-premultiply anymore

width: width * ratio,
height: height * ratio,
channels: 4,
},
})
.jpeg()
.toBuffer();
});

switch (tiletype) {
case "jpg":
return image.jpeg().toBuffer();
case "png":
return image.png().toBuffer();
case "webp":
return image.webp().toBuffer();
}
};

// Generate MBTiles file from a given style, bounds, and zoom range
Expand All @@ -104,11 +92,13 @@ export const generateMBTiles = async (
styleDir,
sourceDir,
bounds,
ratio,
minZoom,
maxZoom,
tempDir,
outputDir,
outputFilename,
tiletype,
) => {
const tempPath = `${tempDir}/${outputFilename}.mbtiles`;
console.log(`Generating MBTiles file: ${tempPath}`);
Expand Down Expand Up @@ -183,6 +173,8 @@ export const generateMBTiles = async (
styleObject,
styleDir,
sourceDir,
ratio,
tiletype,
zoom,
x,
y,
Expand Down
4 changes: 4 additions & 0 deletions src/initiate.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ export const initiateRendering = async (
openStreetMap,
overlay,
bounds,
ratio,
minZoom,
maxZoom,
outputDir,
outputFilename,
tiletype,
) => {
console.log("Initiating rendering...");

Expand Down Expand Up @@ -161,11 +163,13 @@ export const initiateRendering = async (
styleDir,
sourceDir,
bounds,
ratio,
minZoom,
maxZoom,
tempDir,
outputDir,
outputFilename,
tiletype,
);

console.log(
Expand Down
16 changes: 12 additions & 4 deletions src/render_map.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import maplibre from "@maplibre/maplibre-gl-native";

import { calculateNormalizedCenterCoords } from "./tile_calculations.js";
import { requestHandler } from "./request_resources.js";
import { generateJPG } from "./generate_resources.js";
import { generateImage } from "./generate_resources.js";

// Render the map, returning a Promise.
const renderMap = (map, options) => {
Expand All @@ -23,6 +23,8 @@ export const renderTile = async (
styleObject,
styleDir,
sourceDir,
ratio,
tiletype,
zoom,
x,
y,
Expand All @@ -38,7 +40,7 @@ export const renderTile = async (
// MapLibre native documentation: https://github.com/maplibre/maplibre-native/blob/main/platform/node/README.md
const map = new maplibre.Map({
request: requestHandler(styleDir, sourceDir),
ratio: 1,
ratio: ratio,
mode: "tile",
});

Expand All @@ -55,7 +57,13 @@ export const renderTile = async (
// Clean up the map instance to free resources
map.release();

const jpeg = await generateJPG(buffer, tileSize, tileSize, 1);
const image = await generateImage(
buffer,
tiletype,
tileSize,
tileSize,
ratio,
);

return jpeg;
return image;
};