Seeing what Google's free "AI Mode" (Gemini 3.5 Flash) can accomplish with some targeted questioning. Debugging AI code builds character.
Read https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Data-model to understand azgaar's data model. Could we create a webpage that is "browse-only" for an exported azgaar JSON file? The json file will be copied manually to a folder on the web server.
Response:
An exported Azgaar JSON file can be visualized in a browse-only, client-side web application by parsing structural data models, including states, burgs, and cultures, directly from the file. A lightweight frontend using JavaScript fetches the locally hosted JSON, allowing for the display of geographical, political, and cultural information through a searchable UI. For technical details on the data structure, visit GitHub Wiki.
Write the web page code to do that
Response:
Here is a complete, single-file HTML page that reads an Azgaar JSON file hosted on your web server and builds a functional, interactive, browse-only dashboard.
It handles Azgaar's compressed coordinate formats, renders an interactive HTML5 Canvas map, and allows you to click on elements to view full data lists or individual item details.
Implementation Code (index.html)
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Azgaar Map Data Browser</title>
<style>
:root {
--bg-color: #1a1a1a;
--panel-bg: #2d2d2d;
--text-color: #e0e0e0;
--accent-color: #4caf50;
--border-color: #444;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
margin: 0;
padding: 0;
background-color: var(--bg-color);
color: var(--text-color);
display: flex;
height: 100vh;
overflow: hidden;
}
#sidebar {
width: 350px;
background-color: var(--panel-bg);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
height: 100%;
}
.tabs {
display: flex;
background: #222;
border-bottom: 1px solid var(--border-color);
}
.tab-btn {
flex: 1;
padding: 12px;
background: none;
border: none;
color: #888;
cursor: pointer;
font-weight: bold;
transition: all 0.2s;
}
.tab-btn.active {
color: var(--accent-color);
border-bottom: 2px solid var(--accent-color);
background: var(--panel-bg);
}
.tab-content {
flex: 1;
overflow-y: auto;
padding: 15px;
display: none;
}
.tab-content.active { display: block; }
#map-container {
flex: 1;
position: relative;
background: #111;
display: flex;
justify-content: center;
align-items: center;
}
canvas {
box-shadow: 0 0 20px rgba(0,0,0,0.5);
max-width: 100%;
max-height: 100%;
}
.data-item {
padding: 10px;
margin-bottom: 8px;
background: #333;
border-radius: 4px;
cursor: pointer;
border-left: 3px solid transparent;
}
.data-item:hover { border-left-color: var(--accent-color); background: #3a3a3a; }
.details-table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
.details-table td {
padding: 6px;
border-bottom: 1px solid #444;
font-size: 0.9em;
}
.details-table td:first-child { font-weight: bold; color: #aaa; width: 40%; }
#loading {
position: absolute;
background: rgba(0,0,0,0.8);
color: white;
padding: 20px;
border-radius: 8px;
font-size: 1.2em;
z-index: 10;
}
</style>
</head>
<body>
<div id="loading">Loading map data...</div>
<!-- Sidebar Data Browser -->
<div id="sidebar">
<div class="tabs">
<button class="tab-btn active" onclick="switchTab('tab-info')">General</button>
<button class="tab-btn" onclick="switchTab('tab-states')">States</button>
<button class="tab-btn" onclick="switchTab('tab-burgs')">Burgs (Cities)</button>
</div>
<!-- Info Tab -->
<div id="tab-info" class="tab-content active">
<h2>Map Overview</h2>
<div id="general-details"></div>
<hr style="border-color: var(--border-color)">
<h3>Selected Inspect</h3>
<div id="inspect-details">Click a feature on the map or lists to inspect.</div>
</div>
<!-- States Tab -->
<div id="tab-states" class="tab-content">
<h3>Nations & States</h3>
<div id="states-list"></div>
</div>
<!-- Burgs Tab -->
<div id="tab-burgs" class="tab-content">
<h3>Settlements</h3>
<div id="burgs-list"></div>
</div>
</div>
<!-- Interactive Canvas Map Viewport -->
<div id="map-container">
<canvas id="mapCanvas"></canvas>
</div>
<script>
// CONFIGURATION: Set this path to your server's manually copied JSON file
const JSON_FILE_PATH = 'map_data.json';
let mapData = null;
const canvas = document.getElementById('mapCanvas');
const ctx = canvas.getContext('2d');
// Fetch data automatically on load
async function loadMapData() {
try {
const response = await fetch(JSON_FILE_PATH);
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
mapData = await response.json();
document.getElementById('loading').style.display = 'none';
initBrowser();
} catch (error) {
document.getElementById('loading').innerText = `Error loading JSON: ${error.message}. Ensure '${JSON_FILE_PATH}' is in the same folder on your server.`;
console.error(error);
}
}
function initBrowser() {
// Setup canvas size based on Azgaar settings
canvas.width = mapData.info.width || 1000;
canvas.height = mapData.info.height || 700;
renderGeneralInfo();
renderStatesList();
renderBurgsList();
drawMap();
setupMapInteractions();
}
// Tab Switcher Logic
function switchTab(tabId) {
document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active'));
document.querySelectorAll('.tab-btn').forEach(el => el.classList.remove('active'));
document.getElementById(tabId).classList.add('active');
event.target.classList.add('active');
}
// Render Sidebar Info Panel
function renderGeneralInfo() {
const info = mapData.info;
document.getElementById('general-details').innerHTML = `
<table class="details-table">
<tr><td>Map Name</td><td>${info.name || 'Unnamed World'}</td></tr>
<tr><td>Seed</td><td>${info.seed}</td></tr>
<tr><td>Size</td><td>${canvas.width} x ${canvas.height}</td></tr>
<tr><td>Created</td><td>${info.version || 'Unknown Version'}</td></tr>
</table>
`;
}
function renderStatesList() {
const container = document.getElementById('states-list');
container.innerHTML = '';
// State 0 is usually Neutral/Wilderness territory
mapData.states.forEach(state => {
if (!state || state.i === 0) return;
const div = document.createElement('div');
div.className = 'data-item';
div.innerHTML = `<strong>${state.name}</strong> (${state.form || 'State'})<br><small>Capital: ${state.capital || 'None'}</small>`;
div.onclick = () => inspectEntity('state', state);
container.appendChild(div);
});
}
function renderBurgsList() {
const container = document.getElementById('burgs-list');
container.innerHTML = '';
mapData.burgs.forEach(burg => {
if (!burg || !burg.name) return;
const div = document.createElement('div');
div.className = 'data-item';
div.innerHTML = `<strong>${burg.name}</strong><br><small>Population: ${burg.population || 'Unknown'}</small>`;
div.onclick = () => inspectEntity('burg', burg);
container.appendChild(div);
});
}
// Inspect Viewer Panel Data Builder
function inspectEntity(type, data) {
switchTab('tab-info');
const container = document.getElementById('inspect-details');
let html = `<h4>${data.name}</h4><table class="details-table">`;
for (const [key, value] of Object.entries(data)) {
if (typeof value !== 'object') {
html += `<tr><td>${key}</td><td>${value}</td></tr>`;
}
}
html += `</table>`;
container.innerHTML = html;
// Flash feature on map
drawMap();
if (type === 'burg') {
ctx.beginPath();
ctx.arc(data.x, data.y, burgRadius(data) * 2, 0, 2 * Math.PI);
ctx.strokeStyle = '#ff0000';
ctx.lineWidth = 3;
ctx.stroke();
}
}
function burgRadius(burg) {
return burg.capital ? 7 : 4;
}
// Parse Azgaar's packed coordinate strings into explicit numbers
function parseCoordinates(str) {
if (!str) return [];
return str.split(',').map(Number);
}
// Draw Map canvas components
function drawMap() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Fill Canvas with background ocean color
ctx.fillStyle = '#1c3144';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 1. Draw River Paths
if (mapData.rivers) {
ctx.strokeStyle = '#3f88c5';
mapData.rivers.forEach(river => {
// Azgaar rivers store paths as simple coordinate arrays
const coords = parseCoordinates(river.cells);
if (coords.length < 4) return;
ctx.beginPath();
ctx.lineWidth = river.width || 2;
// Mock rendering path line segments sequentially
Use code with caution.
// (THE NEXT SECTION WAS OUTSIDE OF THE CODE BLOCK)
ctx.moveTo(coords[0] || 100, coords[1] || 100);
ctx.stroke();
});
}
// 2. Render Burgs (Settlements)
mapData.burgs.forEach(burg => {
if (!burg || !burg.name) return;
// Draw icon circle marker
ctx.fillStyle = burg.capital ? '#ffcc00' : '#ffffff';
ctx.strokeStyle = '#000000';
ctx.lineWidth = 1;
ctx.beginPath();
const radius = burgRadius(burg);
ctx.arc(burg.x, burg.y, radius, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
// Draw Text labels
ctx.fillStyle = '#ffffff';
ctx.font = burg.capital ? 'bold 11px sans-serif' : '10px sans-serif';
ctx.shadowColor = 'black';
ctx.shadowBlur = 4;
ctx.fillText(burg.name, burg.x + radius + 3, burg.y + 3);
ctx.shadowBlur = 0; // Reset text shadow blur
});
}
// Map Click Interaction Engine
function setupMapInteractions() {
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect();
// Translate click space to inner Canvas coordinate space
const x = (e.clientX - rect.left) * (canvas.width / rect.width);
const y = (e.clientY - rect.top) * (canvas.height / rect.height);
// Detect if user clicked directly onto a Burg coordinate node
const clickedBurg = mapData.burgs.find(burg => {
if (!burg || !burg.name) return false;
const distance = Math.hypot(burg.x - x, burg.y - y);
return distance < 10; // Target hit accuracy radius
});
if (clickedBurg) {
inspectEntity('burg', clickedBurg);
}
});
}
window.onload = loadMapData;
### Setup Instructions
1. **Save the File**: Save the code code segment above as `index.html` on your computer.
2. **Export data**: Open Azgaar's generator, click **Save**, choose **Export as JSON**, and name the output file `map_data.json`.
3. **Move to Folder**: Place both files in the exact same directory folder on your web server.
4. **Access the Page**: Open your browser to the URL address pointing directly to that target path directory.
### Structural Compatibility Notes
* **Data Parsing**: Azgaar utilizes highly optimized packed coordinate formats for complex boundaries (such as `cells` or rivers). The script features an onboarding utility `parseCoordinates()` that handles reading elements explicitly.
* **Inspect Actions**: Clicking a city directly inside the map view snaps your left layout side panel to display demographic, relational ownership, or operational details seamlessly.
Would you like to expand this page by adding **color overlay support for state territories**, or would you like to build an updated filter tool to **search for specific locations by name**?
Copied the index.html (including the HTML outside of the code block)
In Notepad++ reformatted the code from after the code block; a real IDE could probably quickly reformat it.
Noticed all the closing HTML tags were missing; are they optional? I added them.
Saved to a local folder as "index.html"
Saved a copy to "\\wsl.localhost\Debian\home\username\domain.xyz\index.html"
Upload the file from WSL terminal:
scp -p domain.xyz/index.html [Server IPv6 address]:/srv/www/
Browse to domain.xyz and press Ctrl-F5 to force a refresh; F5 wasn't enough
A good error message appear!
Error loading JSON: HTTP error! status: 404. Ensure 'map_data.json' is in the same folder on your server.
Selected Inspect
Click a feature on the map or lists to inspect.
Expand the menu from top-left, click Export
Under Export to JSON, click "full". A JSON file is saved to your Downloads folder.
Copy the JSON file to "\\wsl.localhost\Debian\home\username\domain.xyz\" folder.
Rename the file to map_data.json .
Upload the file from WSL terminal:
scp -p domain.xyz/map_data.json [Server IPv6 address]:/srv/www/
Browse to domain.xyz and press Ctrl-F5
No error message this time! The Map Overview has data, but there is nothing displayed on the screen. So it's time for debugging.
Press Ctrl-Shift-I to user Chrome Dev Tools; some error messages were shown:
TypeError: Cannot read properties of undefined (reading 'forEach')
at renderStatesList ((index):203:28)
at initBrowser ((index):172:13)
at loadMapData ((index):159:17)
Time to get serious: launched Notepad++. It wasn't time to beg Gemini for help yet.
Open the files from WSL: Browse to \\wsl.localhost\Debian\home\username\domain.xyz\
index.html
map_data.json
Check line 203 of index.html
Look for a Notepad++ plugin: https://sourceforge.net/projects/jsminnpp/files/Uni/JSToolNPP.25.11.16.uni.64.zip/download
Create folder C:\Program Files\Notepad++\plugins\JSMinNPP\
Transfer JSMinNPP.dll from the zip file to the new folder
Restart Notepad++
With the JSON file loaded, use Plugins menu, JSTool, JSFormat
Commented out line 172 (renderStatesList), uploaded, and refreshed.
Commented out line 173 (renderBurgsList), uploaded, and refreshed. But it seems like mapData structures aren't being referenced right.
Had to fix some variable names and change mapData to mapData.pack:
191: info.name info.mapName
203: mapData.states mapData.pack.states
216, 286, 318: mapData.burgs mapData.pack.burgs
270, 272,: mapData.rivers mapData.pack.rivers
Re-upload and refresh. Wishing I had finished the VSCodium tutorial first, it should be able to remotely edit files. Not to mention version control!
Now the States and Burgs are populated; There are dots for each burg but no lines or states. The text is dark, on a blue-gray background. There is no zoom. There are no rivers; but the data doesn't have any either!
After a review of the source, I see all of features are working as-implemented.
Time to backup the files and make AI implement more features. I'll start by pointing out the mistakes, and asking it to separate the script from index.html. I expect Gemini should work better if I break the code into small chunks.
One real problem here is the full JSON taking 13 MB. A good system would load these hierarchically.
I corrected some mistakes; mapData.states is mapData.pack.states and mapData.rivers is mapData.pack.rivers. Also info.name is info.mapName. To make this easier, could you fix the mistakes and separte index.html from map_data_render.js ?
It generated index.html and map_data_render.js. I copied them, uploaded to web server, and Ctrl-F5 to refresh again.
scp -p index.html map_data_render.js [fd43:43:43::1]:/srv/www/
Also thinking it's time to look up the Let's Encrypt for local webserver .
No errors, but nothing displayed either. It did not use mapData.pack.burgs, because I did not tell it to. And I found that Ctrl-F5 only reloads index.html now; changes to map_data_render.js were ignored by the cache.
Go to Developer Tools, Network tab. Check the box for "Disable cache". Use F5 to refresh. Now the burgs are loaded.
Also use mapData.pack.burgs .
Add code to draw the states. The states refer to cells, the cells refer to vertices (probably as v). The cells have biomes, which refer to indexes in biomesData; use the color from biomesData for the cells.
It generated index.html and map_data_render.js again. Don't review the code; upload, refresh. But it doesn't have a reference for map_data.json because it's too big; I'll explain the JSON schema. Another prompt suggested some python code to upload it with an API key here: https://ai.google.dev/gemini-api/docs/pricing
The Google AI Studio prompts and responses are used for AI training, so only appropriate for "free as in beer" software. A tutorial for another day. We're using the chat prompt today!
In Notepad++, press Ctrl-M to start a "Mark" search.
Set "Regular Expression" search mode. Find ^( {4}| {8})"[^"\r\n]*" and click Mark All.
Click "Copy Marked Text", and paste into a temporary document for the schema.
Remove irrelevant items from the schema.
map_data.json has these elements that might be relevant; * indicates a child node. Be sure to use mapData.pack where appropriate:
"info"
* "description"
* "mapName"
* "width"
* "height"
* "seed"
* "mapId"
"settings"
* "mapName"
"pack"
* "cells"
* "vertices"
* "features"
* "cultures"
* "burgs"
* "states"
* "provinces"
* "religions"
* "rivers"
* "markers"
* "routes"
* "zones"
"grid"
* "cells"
* "vertices"
* "cellsDesired"
* "spacing"
* "cellsY"
* "cellsX"
* "points"
* "boundary"
* "seed"
* "features"
"biomesData"
* "i"
* "name"
* "color"
* "biomesMatrix"
* "habitability"
* "iconsDensity"
* "icons"
* "cost"
"notes"
"nameBases"
It sent a new map_data_render.js to try. Again with some code overflowed past the end of the code block. And some notes about what it changed:
### What This Fixes
1. **`mapData.pack.vertices`**: Replaced all old root references with the proper child structure.
2. **Flat Vertex Offsets**: Fixed the drawing calculation loop (`vertices.p[vIdx * 2]`) to cleanly alternate coordinates on the canvas context instead of crashing or generating zero-width triangles.
3. **True Biome Identity Strings**: The click handler now pulls from `mapData.biomesData.name` array index offsets to print real text classifications (like *"Temperate Rainforest"* or *"Hot Desert"*) instead of displaying raw integers.
4. **Relational State Information**: Cells now cross-reference their raw state numbers with the `pack.states` list array so clicking anywhere displays the actual name of the nation ruling that territory.
Would you like to build code to display the **Provinces or Religions** map overlays next, or do you want to implement a custom **CSS styling scheme template** to clean up the left layout block interface?
Uploaded and refreshed, but still showing default ocean color. There is code now for drawing the cells, but it's not working. I pasted in some console.log debugging to see how many cells it processed or if it used a fallback.
Actually despite turning off caching on the browser side and Ctrl-F5, it's using old .js; Cloudflare has a "Purge Everything" button which helps. Should have definitely set up the local web server, first!
The "overflow" code had some formatting that ate the real code, so had it do some syntax correction.
This part overflowed your code block of the javascript: // Look up state name owned by this cellconst stateIndex = cells.state ? cells.state[closestCellIndex] : 0;const stateObj = pack.states ? pack.states.find(s => s && s.i === stateIndex) : null;const stateName = stateObj ? stateObj.name : 'Neutral/Wilderness';const cellData = {name: Cell #${closestCellIndex},'Belongs To State': stateName,'Biome Type': biomeName,Height: cells.h ? cells.h[closestCellIndex] : 'N/A',Population: cells.pop ? cells.pop[closestCellIndex] : 'N/A'};inspectEntity('cell', cellData);}}});}window.onload = loadMapData;
It returned just the affected setupMapInteractions function and window.onload event, which I copied and pasted over the old function.
After reloading, now I see the console log messages for the "Fallback", which means its not processing the data structures correctly.
It's forgetting that cells and vertices are arrays, and my "schema" didn't show that. Off to https://app.quicktype.io/?l=schema to analyze the map_data.json. It output a nicely formatted 1878 line JSON schema that I saved as map_data.schema.json
I will paste the complete schema. Some of the code wasn't considering arrays, and using the fallback.
Then I pasted the schema and sent the prompt.
Something went wrong and the content wasn't generated.
Oops, was the schema I pasted too long?
Not at all! Your schema snippet was actually incredibly helpful. Blah blah blah
Please print out the corrected functions.
Here are top web results for exploring this topic:
(Real new question: Should I print the return of a function, or ...)
(Printing a function in Python - Stack Overflow)
... 8 more basic references to programming functions for printing out
I want to see the updated map_data_render.js
The javascript code block overflowed again, but I copied from the previous file to keep the formatting correct.
It still made a mistake regarding the arrays of pack.cells and pack.vertices, but was being extra careful to check if variables existed . Oh well, I'll explicitly point out the problem.
pack.cells and pack.vertices are arrays... so pack.cells.v wouldn't exist, you'd need pack.cells[0].v for index 0, and so on. I think that's why its falling back to ocean colors and not drawing the cells. Please output the javascript again; use separate code blocks when outputting, so that the code block doesn't overflow.
You hit the nail on the head! In many JSON data models, an object array layout would look exactly like pack.cells[i].v.
However, Azgaar's generator uses a highly specific optimization known as a SoA (Structure of Arrays) data model to keep files small and calculations fast. Instead of storing thousands of individual cell objects, pack.cells is actually a single object containing massive, aligned parallel arrays (e.g., pack.cells.v is an array of arrays, pack.cells.biome is a flat array of numbers, etc.).
The problem with the ocean fallback happening is that cells.i.length doesn't exist, as pack.cells.i is a flat array of numeric indices. If pack.cells.i is empty or structured differently, the loop terminates immediately.
And so on. So it politely agreed with my analysis and then said it was wrong, with different conclusion; cool! Gemini then output new code, in 3 separate blocks. Let's try it.
Pasted, uploaded, purged, reloaded. Same result! Oh well, it was wrong and I'll have to fix it myself.
Changed const vertexIndices = cells.v[i]; to const vertexIndices = cells[i].v;
And vertices.p[firstVIdx * 2] to vertices[firstVIdx * 2].p ; which was undefined! I'm not sure why it's multiplying by 2; after setting a breakpoint in Chrome Dev Tools and reloading, I see firstVIdx is a high number; the *2 is a mistaken idea about X and Y coordinates being contained in the "v" element.
Both the AI and I have wrong ideas about vertices and v; Relevant X,Y data seems to be in grid.points. Let's see if it can figure out more than I can at 2:30am.
The point X, Y data is not in pack.vertices" directly; It seems the points are under grid.points array of [X,Y].
vertices array elements have "i" for index, "p" is a 2-element array of two numbers not far from each other. "v" has three elements, which tend to be close together and smaller. "c" also tends to have 3 elements but larger, often in the thousands.
Response:
That makes absolute sense! Azgaar's generator maps its topology by referencing the grid data structure to determine geometry coordinates.
In this structure:
mapData.grid.points is an array containing flat [x, y] pairs for every cell index.
The indices found inside pack.cells.v[i] point directly to objects inside pack.vertices. Their p element contains specific structural localized offset metrics, but the concrete absolute screen values for map cells align with the index vectors of mapData.grid.points[i].
Because pack.cells uses a Structure of Arrays (SoA) layout, individual indices are determined by mapping the lookup position onto pack.cells.biome, pack.cells.state, or pack.cells.v.
...
Key Framework Structural Adjustments
grid.points Polygon Rendering: The renderer now skips the local pack.vertices property math completely, choosing instead to iterate straight through the array indexes inside grid.points[ptIdx] to parse real 2D [X, Y] layout coordinates.
Safer Fallbacks: Handled potential missing array exceptions explicitly (grid.points[firstIdx]) to keep the canvas engine running even if specific coordinates fail to evaluate cleanly.
Let's see if it figured things out. No, till making the mistake with cells.v, I'll edit it again myself. Now, cells are drawn! Completely wrong, and they all seem to be light-blue shades.
Gemini Flash 3.5 started off strong, but stuck with some bad ideas and wasn't able to go in a new direction without my guidance.
The main benefits to me were learning some new features of Notepad++ (and JSTools plugin), Chrome Dev Tools, and finding the Quicktype schema generator. Instead of finding a stronger AI to do the rest for me, I'd like to debug this myself; while asking questions to Google of course.
You can see the results at https://axus.club/map_data_bad/
The real Azgaar website uses path and polygon elements. And it can zoom in!
You are exactly right! The real Azgaar application uses an SVG-based rendering approach rather than an HTML5 Canvas. By using <svg>, <path>, and <polygon> elements, Azgaar's generator achieves razor-sharp visuals, independent scaling, and effortless browser-native mouse zooming. [1]
Converting our architecture to SVG makes handling those coordinate arrays much simpler and gives us crisp pan-and-zoom functionality using a lightweight library like SVG-Pan-Zoom.
Below is the rewritten code, split cleanly into two new files to accommodate the move to SVG.
The new index.html loaded a "script" of https://jsdelivr.net , but just the top-level URL and no script. So it times out. Wishful thinking at its finest ! But, this is preferrable to the security risk of including JS files from random pages. I noticed Gemini is consistent about nerfing includes to the top-level domain.
After a few days with good sleep, I returned to the problem without AI-assistance.
The general concept was right: Loop through all cells, trace lines connecting the vertices of the cell, and then fill the polygon traced from the vertices. I was basically correct, array subscripts have to be used correctly; what I missed was which arrays needed to be looped through. And which element contained the X,Y value of the vertex; vertices[i].p is the point coordinates, cells[i].p is a random center point that can be ignored.
The result is at https://axus.club/map_data_good/
Here is the code I fixed:
function drawMap() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const pack = mapData.pack || {};
//const grid = mapData.grid || {};
const biomesData = mapData.biomesData || {};
// 1. Draw Biome Polygon Cell Layers Safely via grid coordinates
const hasCells = pack.cells ;
const hasVertices = pack.vertices && pack.vertices.length > 0;
if (hasVertices && hasCells) {
const cells = pack.cells;
const totalCells = cells.length;
const verts = pack.vertices;
const totalVerts = verts.length; //This should be equal?
for (let i = 0; i < totalCells; i++) {
// Reference data members
const cellVerts = cells[i].v;
cvLen = cellVerts.length;
if (cvLen === 0) continue;
let cvPoint = verts[cellVerts[0]].p;
// Each cell has a biome index, which determines the biome color
const biomeIndex = cells[i].biome ? cells[i].biome : 0;
let biomeColor = '#1c3144';
const hasColor = biomesData.color && biomesData.color[biomeIndex];
if (hasColor) {
biomeColor = biomesData.color[biomeIndex];
}
// Map tracing starts at the first vertex point
ctx.beginPath();
ctx.moveTo(cvPoint[0], cvPoint[1]);
// Trace polygon lines to remaining vertices
for (let j = 1; j < cvLen; j++) {
const nvIndex = cellVerts[j];
if (verts[nvIndex].p) {
ctx.lineTo(verts[nvIndex].p[0], verts[nvIndex].p[1]);
}
}
ctx.closePath();
ctx.fillStyle = biomeColor;
ctx.fill();
// Draw borders later?
//ctx.strokeStyle = biomeColor;
//ctx.lineWidth = 0.5;
//ctx.stroke();
}
} else {
// No data, nothing but sea
ctx.fillStyle = '#1c3144';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}