#target photoshop /** * assemble-map-tiles.jsx * * Assembles map tiles downloaded by the Map Download Tool into a single * Photoshop document. Tile filenames must follow the pattern: z_x_y.png * * Usage: File > Scripts > Browse... and select this file. * For large tile sets (100+ tiles), assembly may take several minutes. */ (function () { var TILE_SIZE = 256; // ── 1. Select folder ────────────────────────────────────────────────────── var folder = Folder.selectDialog( "Select the folder containing your map tile files (z_x_y.png)" ); if (!folder) { return; } // user cancelled // ── 2. Collect and parse tile files ────────────────────────────────────── var allFiles = folder.getFiles("*.png"); if (!allFiles || allFiles.length === 0) { alert("No PNG files found in:\n" + folder.fsName); return; } var tiles = []; var re = /^(\d+)_(\d+)_(\d+)\.png$/i; for (var i = 0; i < allFiles.length; i++) { if (!(allFiles[i] instanceof File)) { continue; } var m = allFiles[i].name.match(re); if (!m) { continue; } tiles.push({ file: allFiles[i], z: parseInt(m[1], 10), x: parseInt(m[2], 10), y: parseInt(m[3], 10) }); } if (tiles.length === 0) { alert("No files matching the pattern z_x_y.png were found in:\n" + folder.fsName); return; } // ── 3. Compute grid bounds ──────────────────────────────────────────────── var xMin = tiles[0].x, xMax = tiles[0].x; var yMin = tiles[0].y, yMax = tiles[0].y; for (var i = 1; i < tiles.length; i++) { if (tiles[i].x < xMin) { xMin = tiles[i].x; } if (tiles[i].x > xMax) { xMax = tiles[i].x; } if (tiles[i].y < yMin) { yMin = tiles[i].y; } if (tiles[i].y > yMax) { yMax = tiles[i].y; } } var cols = xMax - xMin + 1; var rows = yMax - yMin + 1; var docWidth = cols * TILE_SIZE; var docHeight = rows * TILE_SIZE; // ── 4. Create destination document ─────────────────────────────────────── var savedUnits = app.preferences.rulerUnits; app.preferences.rulerUnits = Units.PIXELS; var mainDoc = app.documents.add( docWidth, docHeight, 72, "Assembled Map Tiles", NewDocumentMode.RGB, DocumentFill.WHITE ); // ── 5. Place each tile as its own layer ─────────────────────────────────── for (var i = 0; i < tiles.length; i++) { var t = tiles[i]; var destX = (t.x - xMin) * TILE_SIZE; var destY = (t.y - yMin) * TILE_SIZE; // Open tile, copy all pixels, close var tileDoc = app.open(t.file); app.activeDocument = tileDoc; tileDoc.selection.selectAll(); tileDoc.selection.copy(); tileDoc.close(SaveOptions.DONOTSAVECHANGES); // Paste into mainDoc as a new layer (centered by Photoshop) app.activeDocument = mainDoc; mainDoc.selection.deselect(); mainDoc.paste(); var newLayer = mainDoc.activeLayer; newLayer.name = t.z + "_" + t.x + "_" + t.y; // Translate from where Photoshop centered it to the correct grid position var b = newLayer.bounds; var offX = destX - parseFloat(b[0]); var offY = destY - parseFloat(b[1]); newLayer.translate(offX, offY); } // ── 6. Restore ruler units ──────────────────────────────────────────────── app.preferences.rulerUnits = savedUnits; // ── 7. Done ─────────────────────────────────────────────────────────────── alert( "Assembly complete!\n\n" + tiles.length + (tiles.length === 1 ? " tile" : " tiles") + " placed\n" + cols + " \xd7 " + rows + " grid (" + docWidth + " \xd7 " + docHeight + " px)" ); }());