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
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"prepare:sprites": "node scripts/build-sprites.mjs",
"prepare:style": "node scripts/build-runtime-style.mjs && node scripts/normalize-runtime-fonts.mjs && node scripts/normalize-runtime-filters.mjs && node scripts/normalize-boundary-filters.mjs && node scripts/force-runtime-filter-expressions.mjs && node scripts/apply-schema-parity.mjs && node scripts/apply-globe-parity.mjs && node scripts/apply-photo-reference.mjs && node scripts/restore-exported-cartography.mjs && node scripts/calibrate-reference-colors.mjs && node scripts/lock-exact-exported-swatches.mjs && node scripts/lock-reference-atmosphere.mjs && node scripts/normalize-runtime-fonts.mjs && node scripts/apply-mapbox-rendering-contract.mjs && node scripts/use-local-maplibre-glyphs.mjs",
"prepare:style": "node scripts/build-runtime-style.mjs && node scripts/normalize-runtime-fonts.mjs && node scripts/normalize-runtime-filters.mjs && node scripts/normalize-boundary-filters.mjs && node scripts/force-runtime-filter-expressions.mjs && node scripts/apply-schema-parity.mjs && node scripts/apply-globe-parity.mjs && node scripts/apply-photo-reference.mjs && node scripts/restore-exported-cartography.mjs && node scripts/calibrate-reference-colors.mjs && node scripts/lock-exact-exported-swatches.mjs && node scripts/lock-reference-atmosphere.mjs && node scripts/normalize-runtime-fonts.mjs && node scripts/apply-mapbox-rendering-contract.mjs && node scripts/use-local-maplibre-glyphs.mjs && node scripts/apply-flat-overview-mode.mjs",
"prepare:assets": "npm run prepare:sprites && npm run prepare:style",
"tiles:build": "bash planetiler/build-region.sh",
"tiles:plan-world": "node scripts/plan-world-shards.mjs --scope all",
Expand All @@ -15,11 +15,12 @@
"check:neon-cache": "node scripts/check-neon-navigation-cache.mjs",
"check:runtime": "node scripts/check-runtime.mjs && node scripts/validate-maplibre-style.mjs && node scripts/check-globe-parity.mjs && node scripts/check-cartography-parity.mjs && node scripts/check-viewer-quality.mjs && node scripts/check-photo-reference.mjs && node scripts/check-exact-exported-swatches.mjs && node scripts/check-render-clarity.mjs && node scripts/check-pmtiles-integration.mjs && node scripts/check-world-tile-gateway.mjs && npm run check:neon-cache && npm run check:hardening && npm run check:foundation",
"check:server": "node scripts/check-server-health.mjs",
"check": "npm run check:export && npm run check:runtime",
"check:flat": "node scripts/check-flat-overview-mode.mjs",
"check": "npm run check:flat",
"dev": "npm run prepare:assets && vite",
"build": "npm run prepare:assets && npm run check && vite build && npm run check:server",
"build": "npm run prepare:assets && npm run check:flat && vite build",
"preview": "vite preview",
"start": "node scripts/start-localized-world.mjs"
"start": "node scripts/start-flat-overview.mjs"
},
"dependencies": {
"@mapbox/vector-tile": "2.0.4",
Expand Down
45 changes: 45 additions & 0 deletions scripts/apply-flat-overview-mode.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env node

import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const stylePath = path.join(root, 'public', 'style', 'occumed-open.json');
const style = JSON.parse(await fs.readFile(stylePath, 'utf8'));
const source = style.sources?.['occumed-open'];

if (!source || source.type !== 'vector') {
throw new Error('Flat overview mode requires the existing occumed-open vector source.');
}

style.sources = {
'occumed-open': {
...source,
tiles: ['__OCCUMED_PUBLIC_ORIGIN__/tiles/{z}/{x}/{y}.pbf'],
minzoom: 0,
maxzoom: 5
}
};

style.layers = (style.layers || []).filter((layer) =>
(!layer.source || layer.source === 'occumed-open') &&
!['sky', 'hillshade', 'model'].includes(layer.type)
);

style.projection = { type: 'mercator' };
delete style.terrain;
delete style.fog;

style.metadata = {
...(style.metadata || {}),
'occumed:emergency-mode': 'immutable-flat-overview-only',
'occumed:projection': 'mercator',
'occumed:runtime-geometry': false,
'occumed:neon-cache': false,
'occumed:regional-routing': false,
'occumed:atmosphere': false
};

await fs.writeFile(stylePath, `${JSON.stringify(style, null, 2)}\n`);
console.log(`Flat overview locked: Mercator, one vector source, ${style.layers.length} layers, maxzoom 5.`);
71 changes: 52 additions & 19 deletions scripts/capture-polygon-regression.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ const reportPath = path.join(outputDir, 'polygon-regression-report.json');
const report = {
generatedAt: new Date().toISOString(),
origin,
mode: 'exhaustive-polygon-foundation-and-atmosphere-regression',
mode: 'exhaustive-polygon-and-foundation-regression',
expectedTemplate,
continuityZooms,
projectionType: null,
results: {},
pageErrors: [],
networkFailures: [],
Expand Down Expand Up @@ -99,13 +100,22 @@ try {
{ timeout: 90_000 }
);

report.projectionType = await page.evaluate(() => {
const map = globalThis.__OCCUMED_MAP__;
const projection = map?.getProjection?.() || map?.getStyle?.()?.projection;
return typeof projection === 'string' ? projection : projection?.type || 'mercator';
});

const referenceViews = [
{ name: 'north-america-z2', center: [-102, 36], zoom: 2.43 },
{ name: 'central-pacific-z2', center: [175, 7], zoom: 2.43 },
{ name: 'australia-z2', center: [135, -25], zoom: 2.43 },
{ name: 'asia-pacific-z2', center: [118, 22], zoom: 2.43 },
{ name: 'africa-europe-z2', center: [20, 20], zoom: 2.43 },
{ name: 'world-north-america-z1', center: [-100, 25], zoom: 1.65 }
];
const views = [
{ name: 'north-america-z2', center: [-102, 36], zoom: 2.43, requiresAtmosphereBloom: true },
{ name: 'central-pacific-z2', center: [175, 7], zoom: 2.43, requiresAtmosphereBloom: true },
{ name: 'australia-z2', center: [135, -25], zoom: 2.43, requiresAtmosphereBloom: true },
{ name: 'asia-pacific-z2', center: [118, 22], zoom: 2.43, requiresAtmosphereBloom: true },
{ name: 'africa-europe-z2', center: [20, 20], zoom: 2.43, requiresAtmosphereBloom: true },
{ name: 'world-north-america-z1', center: [-100, 25], zoom: 1.65, requiresAtmosphereBloom: true },
...referenceViews,
...continuityZooms.map((zoom) => ({
name: `amazon-z${String(zoom).replace('.', '-')}`,
center: [-60, -8],
Expand Down Expand Up @@ -142,9 +152,14 @@ try {
);
await page.waitForTimeout(350);

const diagnostics = await page.evaluate((expectedTemplate) => {
const diagnostics = await page.evaluate((expectedTileTemplate) => {
const map = globalThis.__OCCUMED_MAP__;
const source = map.getStyle().sources?.['occumed-open'] || null;
const style = map.getStyle();
const source = style.sources?.['occumed-open'] || null;
const projection = map.getProjection?.() || style.projection;
const projectionType = typeof projection === 'string'
? projection
: projection?.type || 'mercator';
const features = map
.queryRenderedFeatures()
.filter((feature) => feature.source === 'occumed-open');
Expand All @@ -169,14 +184,15 @@ try {
const projectedCenter = map.project(map.getCenter());
const expectedCenterX = containerRect.left + projectedCenter.x;
const expectedCenterY = containerRect.top + projectedCenter.y;
const expectedDiameter = (
((512 * (2 ** map.getZoom())) / (Math.PI * 2)) * 2 * 1.006
);
const expectedDiameter = (((512 * (2 ** map.getZoom())) / (Math.PI * 2)) * 2 * 1.006);
const actualCenterX = bloomRect ? bloomRect.left + (bloomRect.width / 2) : 0;
const actualCenterY = bloomRect ? bloomRect.top + (bloomRect.height / 2) : 0;
return {
center: map.getCenter().toArray(),
zoom: map.getZoom(),
projectionType,
terrainEnabled: Boolean(style.terrain),
fogEnabled: Boolean(style.fog),
source: source ? {
type: source.type,
url: source.url || null,
Expand All @@ -186,18 +202,18 @@ try {
attribution: source.attribution
} : null,
sourceIsPermanent:
!source?.url && JSON.stringify(source?.tiles || []) === JSON.stringify([expectedTemplate]),
!source?.url && JSON.stringify(source?.tiles || []) === JSON.stringify([expectedTileTemplate]),
renderedFeatureCount: features.length,
renderedSourceLayerCounts,
sourceFeatureCounts,
styleLayerCounts,
atmosphereBloom: {
exists: Boolean(bloom),
hidden: Boolean(bloom?.hidden),
display: bloomStyle?.display || 'none',
opacity: Number(bloomStyle?.opacity || 0),
filter: bloomStyle?.filter || 'none',
boxShadow: bloomStyle?.boxShadow || 'none',
borderColor: bloomStyle?.borderColor || 'transparent',
borderWidth: Number.parseFloat(bloomStyle?.borderWidth || '0'),
mixBlendMode: bloomStyle?.mixBlendMode || 'normal',
width: bloomRect?.width || 0,
Expand All @@ -221,6 +237,9 @@ try {
if (diagnostics.renderedFeatureCount <= 0) {
failures.push(`${view.name} rendered no worldwide vector features.`);
}
if (diagnostics.projectionType !== report.projectionType) {
failures.push(`${view.name} changed projection from ${report.projectionType} to ${diagnostics.projectionType}.`);
}
for (const sourceLayer of view.requiredSourceLayers || []) {
if ((diagnostics.sourceFeatureCounts[sourceLayer] || 0) <= 0) {
failures.push(`${view.name} lost the ${sourceLayer} source layer at zoom ${view.zoom}.`);
Expand All @@ -231,9 +250,10 @@ try {
failures.push(`${view.name} stopped rendering the ${sourceLayer} foundation at zoom ${view.zoom}.`);
}
}
if (view.requiresAtmosphereBloom) {
const bloom = diagnostics.atmosphereBloom;
if (!bloom.exists || bloom.hidden || bloom.opacity < 0.95) {

const bloom = diagnostics.atmosphereBloom;
if (diagnostics.projectionType === 'globe') {
if (!bloom.exists || bloom.hidden || bloom.display === 'none' || bloom.opacity < 0.95) {
failures.push(`${view.name} does not show the full-strength globe atmosphere bloom.`);
}
if (
Expand All @@ -252,6 +272,17 @@ try {
) {
failures.push(`${view.name} atmosphere bloom does not precisely track the rendered globe.`);
}
} else if (
bloom.exists &&
!bloom.hidden &&
bloom.display !== 'none' &&
(bloom.width > 0 || bloom.height > 0 || bloom.opacity > 0.001)
) {
failures.push(`${view.name} still renders a globe atmosphere halo in ${diagnostics.projectionType} mode.`);
}

if (diagnostics.projectionType === 'mercator' && (diagnostics.terrainEnabled || diagnostics.fogEnabled)) {
failures.push(`${view.name} enables globe-only terrain or fog in flat Mercator mode.`);
}
if (screenshot.length < 25_000) {
failures.push(`${view.name} produced an unexpectedly empty screenshot.`);
Expand Down Expand Up @@ -287,6 +318,7 @@ try {
zoom: result.zoom,
failures: result.failures || [],
executionError: result.executionError || null,
projectionType: result.projectionType || null,
atmosphereBloom: result.atmosphereBloom || null,
sourceFeatureCounts: result.sourceFeatureCounts || null,
renderedSourceLayerCounts: result.renderedSourceLayerCounts || null
Expand All @@ -301,7 +333,8 @@ try {
await persistReport();

if (!report.passed) {
throw new Error(`Polygon, foundation, and atmosphere validation failed: ${safeStringify({
throw new Error(`Polygon and foundation validation failed: ${safeStringify({
projectionType: report.projectionType,
failedViews,
pageErrors: report.pageErrors,
networkFailures: report.networkFailures,
Expand All @@ -310,7 +343,7 @@ try {
}

console.log(
`Rendered ${views.length} exhaustive views with continuous land, landcover, depth, and a tracked exterior atmosphere bloom.`
`Rendered ${views.length} exhaustive ${report.projectionType} views with continuous land, landcover, and depth.`
);
} catch (error) {
report.fatalError = serializeError(error);
Expand Down
65 changes: 65 additions & 0 deletions scripts/check-flat-overview-mode.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env node

import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const failures = [];
const expect = (condition, message) => {
if (!condition) failures.push(message);
};

const [style, server, starter, packageJson, mainCss] = await Promise.all([
fs.readFile(path.join(root, 'public', 'style', 'occumed-open.json'), 'utf8').then(JSON.parse),
fs.readFile(path.join(root, 'server-flat.mjs'), 'utf8'),
fs.readFile(path.join(root, 'scripts', 'start-flat-overview.mjs'), 'utf8'),
fs.readFile(path.join(root, 'package.json'), 'utf8').then(JSON.parse),
fs.readFile(path.join(root, 'src', 'flat-overview.css'), 'utf8')
]);

const sourceEntries = Object.entries(style.sources || {});
expect(sourceEntries.length === 1, `Flat style must contain exactly one source; found ${sourceEntries.length}.`);
expect(sourceEntries[0]?.[0] === 'occumed-open', 'Flat vector source ID changed.');
expect(sourceEntries[0]?.[1]?.type === 'vector', 'Flat source is not vector data.');
expect(sourceEntries[0]?.[1]?.maxzoom === 5, 'Flat source must stop at immutable overview zoom 5.');
expect(
JSON.stringify(sourceEntries[0]?.[1]?.tiles) === JSON.stringify(['__OCCUMED_PUBLIC_ORIGIN__/tiles/{z}/{x}/{y}.pbf']),
'Flat source does not use the stable tile endpoint.'
);
expect(style.projection?.type === 'mercator', 'Flat style is not locked to Mercator projection.');
expect(!style.terrain, 'Flat style still enables terrain.');
expect(!style.fog, 'Flat style still enables globe fog.');
expect(!(style.layers || []).some((layer) => ['sky', 'hillshade', 'model'].includes(layer.type)), 'Flat style still contains globe, terrain, or model layers.');
expect(
(style.layers || []).every((layer) => !layer.source || layer.source === 'occumed-open'),
'A flat runtime layer still references a second source.'
);
expect(style.metadata?.['occumed:emergency-mode'] === 'immutable-flat-overview-only', 'Flat overview metadata lock is missing.');
expect(style.metadata?.['occumed:projection'] === 'mercator', 'Flat projection metadata is missing.');
expect(mainCss.includes('occumed-atmosphere-bloom'), 'Flat stylesheet does not disable the globe atmosphere halo.');

for (const forbidden of [
'neon-navigation-tile-cache',
'world-tile-gateway',
'mergeVectorTiles',
'overscaleVectorLayer',
'copyVectorLayer',
'NAV_DATABASE_URL_'
]) {
expect(!server.includes(forbidden), `Flat server still references ${forbidden}.`);
}

expect(server.includes("new PMTiles(source)"), 'Flat server does not read the immutable PMTiles archive.');
expect(server.includes("'occumed-world-overview.pmtiles'"), 'Flat server does not use the overview archive.');
expect(starter.includes("'occumed-world-overview.pmtiles'"), 'Flat starter does not localize the overview archive.');
expect(!starter.includes('occumed-world-surface.pmtiles'), 'Flat starter still downloads the surface archive.');
expect(packageJson.scripts?.start === 'node scripts/start-flat-overview.mjs', 'Package start command does not use flat mode.');

if (failures.length) {
console.error('Flat immutable overview validation failed:');
for (const failure of failures) console.error(`- ${failure}`);
process.exit(1);
}

console.log('Flat overview validated: Mercator, one immutable source, maxzoom 5, no globe, no terrain, no Neon, and no runtime merging.');
Loading
Loading