diff --git a/package-lock.json b/package-lock.json
index 21b1da70..8232e97b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,7 @@
"devDependencies": {
"@lucide/svelte": "^1.14.0",
"@openmeteo/file-reader": "^0.0.16",
- "@openmeteo/weather-map-layer": "github:open-meteo/weather-map-layer#e65e07029794285a64ec19f5f96e0e0c18a3e539",
+ "@openmeteo/weather-map-layer": "github:open-meteo/weather-map-layer#bae864da1e16b596b0232fc733dd54a73eb69676",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.59.0",
"@sveltejs/vite-plugin-svelte": "^7.0.0",
@@ -675,8 +675,8 @@
},
"node_modules/@openmeteo/weather-map-layer": {
"version": "0.0.19",
- "resolved": "git+ssh://git@github.com/open-meteo/weather-map-layer.git#e65e07029794285a64ec19f5f96e0e0c18a3e539",
- "integrity": "sha512-Ds6x5mYsa5XbYALlvoU2x8S9V1/6Y4oQjXy94qlsRoMGVIiMB3szxZpjx6z06DpUJdRvAzHiT1qXAPBXTrK9YQ==",
+ "resolved": "git+ssh://git@github.com/open-meteo/weather-map-layer.git#bae864da1e16b596b0232fc733dd54a73eb69676",
+ "integrity": "sha512-nhzww5pSdY+O50r8A9zBRYxxcqUfVqgHzbs1LfzGCZLgqO6+NIMVvspFVDWZ2ZPE0AEc0uR/ytFXH2PIWbhyQQ==",
"dev": true,
"dependencies": {
"@mapbox/tilebelt": "^2.0.3",
diff --git a/package.json b/package.json
index 809b6859..4a7e1152 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,7 @@
"devDependencies": {
"@lucide/svelte": "^1.14.0",
"@openmeteo/file-reader": "^0.0.16",
- "@openmeteo/weather-map-layer": "github:open-meteo/weather-map-layer#e65e07029794285a64ec19f5f96e0e0c18a3e539",
+ "@openmeteo/weather-map-layer": "github:open-meteo/weather-map-layer#bae864da1e16b596b0232fc733dd54a73eb69676",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.59.0",
"@sveltejs/vite-plugin-svelte": "^7.0.0",
diff --git a/src/lib/clipping.ts b/src/lib/clipping.ts
index 4c05e7c7..a33d6e80 100644
--- a/src/lib/clipping.ts
+++ b/src/lib/clipping.ts
@@ -86,7 +86,9 @@ export const buildCountryClippingOptions = (countries: Country[]): ClippingOptio
if (!geometry) return false;
const lons: number[] = [];
- const collect = (coords: any): void => {
+ const collect = (
+ coords: number | number[] | number[][] | number[][][] | number[][][][]
+ ): void => {
if (!Array.isArray(coords)) return;
if (typeof coords[0] === 'number') {
// a single position [lon, lat]
@@ -102,7 +104,8 @@ export const buildCountryClippingOptions = (countries: Country[]): ClippingOptio
collect(geometry.coordinates as number[][][][]);
} else if (geometry.type === 'GeometryCollection') {
for (const g of geometry.geometries) {
- if (g.type === 'Polygon' || g.type === 'MultiPolygon') collect((g as any).coordinates);
+ if (g.type === 'Polygon') collect(g.coordinates);
+ else if (g.type === 'MultiPolygon') collect(g.coordinates);
}
}
diff --git a/src/lib/components/selection/variable-selection.svelte b/src/lib/components/selection/variable-selection.svelte
index d2233f27..9e263300 100644
--- a/src/lib/components/selection/variable-selection.svelte
+++ b/src/lib/components/selection/variable-selection.svelte
@@ -251,7 +251,9 @@
variant="outline"
class="bg-glass/75 dark:bg-glass/75 backdrop-blur-sm shadow-md {variableSelectionOpen
? 'bg-glass/95!'
- : ''} hover:bg-glass/95! h-7.25 w-45 cursor-pointer justify-between rounded border-none p-1.5! {domainSelectionOpen ? 'hidden' : ''}"
+ : ''} hover:bg-glass/95! h-7.25 w-45 cursor-pointer justify-between rounded border-none p-1.5! {domainSelectionOpen
+ ? 'hidden'
+ : ''}"
role="combobox"
aria-expanded={variableSelectionOpen}
>
@@ -381,7 +383,10 @@
variant="outline"
class="bg-glass/75 dark:bg-glass/75 backdrop-blur-sm shadow-md {pressureLevelSelectionOpen
? 'bg-glass/95!'
- : ''} hover:bg-glass/95! h-7.25 w-45 cursor-pointer justify-between rounded border-none p-1.5! {domainSelectionOpen || variableSelectionOpen ? 'hidden' : ''}"
+ : ''} hover:bg-glass/95! h-7.25 w-45 cursor-pointer justify-between rounded border-none p-1.5! {domainSelectionOpen ||
+ variableSelectionOpen
+ ? 'hidden'
+ : ''}"
role="combobox"
aria-expanded={pressureLevelSelectionOpen}
>
diff --git a/src/lib/components/settings/seamless-border-settings.svelte b/src/lib/components/settings/seamless-border-settings.svelte
new file mode 100644
index 00000000..639605a1
--- /dev/null
+++ b/src/lib/components/settings/seamless-border-settings.svelte
@@ -0,0 +1,33 @@
+
+
+{#if $isSeamless}
+
+
Seamless Borders
+
+ {
+ updateSeamlessBorderLayer();
+ }}
+ />
+
+
+
+{/if}
diff --git a/src/lib/components/settings/settings.svelte b/src/lib/components/settings/settings.svelte
index 9d137dc6..d54626eb 100644
--- a/src/lib/components/settings/settings.svelte
+++ b/src/lib/components/settings/settings.svelte
@@ -9,6 +9,7 @@
import GridSettings from './grid-settings.svelte';
import OpacitySetting from './opacity-setting.svelte';
import PopupSettings from './popup-settings.svelte';
+ import SeamlessBorderSettings from './seamless-border-settings.svelte';
import StateSettings from './state-settings.svelte';
import TileSizeSettings from './tile-size-settings.svelte';
import UnitSettings from './unit-settings.svelte';
@@ -25,6 +26,7 @@
+
diff --git a/src/lib/components/time/time-selector.svelte b/src/lib/components/time/time-selector.svelte
index 91a5b946..beba8509 100644
--- a/src/lib/components/time/time-selector.svelte
+++ b/src/lib/components/time/time-selector.svelte
@@ -181,7 +181,7 @@
const checkClosestModelRun = async () => {
let timeStep = new Date($time);
- let nearestModelRun = closestModelRun(timeStep, $selectedDomain.model_interval);
+ let nearestModelRun = closestModelRun(timeStep, $selectedDomain.model_interval!);
if (nearestModelRun.getTime() > latestReferenceTime.getTime()) {
nearestModelRun = latestReferenceTime;
}
@@ -194,7 +194,7 @@
toast.warning('Date selected too old, using 7 days ago time');
const nowTimeStep = domainStep(
new Date(date7DaysAgo),
- $selectedDomain.time_interval,
+ $selectedDomain.time_interval!,
'floor'
);
time.set(nowTimeStep);
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index 03b43cd4..1259f97a 100644
--- a/src/lib/constants.ts
+++ b/src/lib/constants.ts
@@ -17,7 +17,8 @@ export const DEFAULT_PREFERENCES = {
terrain: false,
hillshade: false,
clipWater: false,
- showScale: true
+ showScale: true,
+ showSeamlessBorders: true
};
// Layer names for map rendering
diff --git a/src/lib/layers.ts b/src/lib/layers.ts
index 6d92dffe..970df616 100644
--- a/src/lib/layers.ts
+++ b/src/lib/layers.ts
@@ -1,11 +1,13 @@
import { get } from 'svelte/store';
+import { type Domain, GridFactory, type SeamlessDomain } from '@openmeteo/weather-map-layer';
import * as maplibregl from 'maplibre-gl';
import { mode } from 'mode-watcher';
import { toast } from 'svelte-sonner';
import { map as m } from '$lib/stores/map';
import { loading, opacity, preferences as p } from '$lib/stores/preferences';
+import { selectedDomain } from '$lib/stores/variables';
import { vectorOptions as vO } from '$lib/stores/vector';
import {
@@ -17,6 +19,7 @@ import {
import { type SlotLayer, SlotManager } from '$lib/slot-manager';
import { refreshPopup } from './popup';
+import { omProtocolSettings } from './stores/om-protocol-settings';
import { currentOmUrl } from './stores/om-url';
import { getOMUrl } from './url';
@@ -285,6 +288,151 @@ export const createManagers = (): void => {
});
};
+// =============================================================================
+// Seamless domain border overlay
+// =============================================================================
+
+const SEAMLESS_BORDER_SOURCE_ID = 'seamlessBorderSource';
+
+const removeSeamlessBorderLayer = (): void => {
+ const map = get(m);
+ if (!map) return;
+ // Collect IDs first to avoid mutating the layer list while iterating
+ const toRemove = (map.getStyle()?.layers ?? [])
+ .map((l) => l.id)
+ .filter((id) => id.startsWith('seamless-border-'));
+ for (const id of toRemove) {
+ if (map.getLayer(id)) map.removeLayer(id);
+ }
+ if (map.getSource(SEAMLESS_BORDER_SOURCE_ID)) map.removeSource(SEAMLESS_BORDER_SOURCE_ID);
+};
+
+export const updateSeamlessBorderLayer = (): void => {
+ const map = get(m);
+ if (!map) return;
+
+ removeSeamlessBorderLayer();
+
+ const preferences = get(p);
+ if (!preferences.showSeamlessBorders) return;
+
+ const domain = get(selectedDomain);
+ if (!('layers' in domain)) return; // Not a seamless domain — nothing to draw
+
+ const seamlessDomain = domain as SeamlessDomain;
+ const settings = get(omProtocolSettings);
+
+ // Build a bounding-box polygon for each sub-layer except the global fallback
+ // (last layer), which covers the whole world and needs no border.
+ const features: GeoJSON.Feature[] = [];
+ for (let i = 0; i < seamlessDomain.layers.length - 1; i++) {
+ const layer = seamlessDomain.layers[i];
+ const concreteDomain = settings.domainOptions.find(
+ (d) => d.value === layer.domainValue && !('layers' in d)
+ ) as Domain | undefined;
+ if (!concreteDomain) continue;
+
+ const [minLon, minLat, maxLon, maxLat] = GridFactory.create(
+ concreteDomain.grid,
+ null
+ ).getBounds();
+ features.push({
+ type: 'Feature',
+ geometry: {
+ type: 'Polygon',
+ coordinates: [
+ [
+ [minLon, minLat],
+ [maxLon, minLat],
+ [maxLon, maxLat],
+ [minLon, maxLat],
+ [minLon, minLat]
+ ]
+ ]
+ },
+ properties: {
+ layerIndex: i,
+ minZoom: layer.minZoom,
+ label: concreteDomain.label ?? concreteDomain.value
+ }
+ });
+ }
+
+ if (features.length === 0) return;
+
+ map.addSource(SEAMLESS_BORDER_SOURCE_ID, {
+ type: 'geojson',
+ data: { type: 'FeatureCollection', features }
+ });
+
+ // Add one line + one symbol MapLibre layer per boundary so each can carry its
+ // own zoom-dependent opacity that fades in 2 zoom levels before the sub-domain
+ // becomes active (i.e. when its minZoom threshold is reached by the user).
+ const lineColor = isDark() ? 'rgba(255,255,255,0.7)' : 'rgba(0,0,0,0.5)';
+ const textColor = isDark() ? 'rgba(255,255,255,0.9)' : 'rgba(0,0,0,0.75)';
+ const textHalo = isDark() ? 'rgba(0,0,0,0.5)' : 'rgba(255,255,255,0.5)';
+
+ for (const feature of features) {
+ const i = feature.properties!.layerIndex as number;
+ const minZoom = feature.properties!.minZoom as number;
+ // Start fading in 2 zoom levels before the layer becomes active
+ const fadeStart = Math.max(0, minZoom - 2);
+
+ // When fadeStart === minZoom (only theoretically possible at minZoom 0),
+ // skip the interpolation and show at full opacity immediately.
+ const opacityExpr: maplibregl.ExpressionSpecification | number =
+ fadeStart < minZoom
+ ? (['interpolate', ['linear'], ['zoom'], fadeStart, 0, minZoom, 1] as const)
+ : 1;
+
+ // Dashed bounding-box border
+ map.addLayer(
+ {
+ id: `seamless-border-line-${i}`,
+ type: 'line',
+ source: SEAMLESS_BORDER_SOURCE_ID,
+ minzoom: fadeStart,
+ filter: ['==', ['get', 'layerIndex'], i],
+ paint: {
+ 'line-color': lineColor,
+ 'line-width': 1.5,
+ 'line-dasharray': [4, 3],
+ 'line-opacity': opacityExpr
+ }
+ },
+ BEFORE_LAYER_VECTOR
+ );
+
+ // Domain name label placed along the border line
+ map.addLayer(
+ {
+ id: `seamless-border-label-${i}`,
+ type: 'symbol',
+ source: SEAMLESS_BORDER_SOURCE_ID,
+ minzoom: fadeStart,
+ filter: ['==', ['get', 'layerIndex'], i],
+ layout: {
+ 'text-field': ['get', 'label'],
+ 'text-size': 11,
+ 'symbol-placement': 'line',
+ 'symbol-spacing': 400,
+ 'text-rotation-alignment': 'map',
+ 'text-offset': [0, -0.8],
+ 'text-allow-overlap': false,
+ 'text-ignore-placement': true
+ },
+ paint: {
+ 'text-color': textColor,
+ 'text-halo-color': textHalo,
+ 'text-halo-width': 1.5,
+ 'text-opacity': opacityExpr
+ }
+ },
+ BEFORE_LAYER_VECTOR
+ );
+ }
+};
+
// =============================================================================
// Public layer API
// =============================================================================
@@ -296,6 +444,7 @@ export const addOmFileLayers = (): void => {
createManagers();
rasterManager?.update('om://' + omUrl);
vectorManager?.update('om://' + omUrl);
+ updateSeamlessBorderLayer();
};
export const changeOMfileURL = (vectorOnly = false, rasterOnly = false): void => {
@@ -316,4 +465,5 @@ export const changeOMfileURL = (vectorOnly = false, rasterOnly = false): void =>
if (!vectorOnly) rasterManager?.update('om://' + omUrl);
if (!rasterOnly) vectorManager?.update('om://' + omUrl);
+ updateSeamlessBorderLayer();
};
diff --git a/src/lib/metadata.ts b/src/lib/metadata.ts
index a93ab38e..bc057e18 100644
--- a/src/lib/metadata.ts
+++ b/src/lib/metadata.ts
@@ -8,13 +8,25 @@ import { domain as d, selectedDomain, variable as v } from '$lib/stores/variable
import { fmtModelRun, getBaseUri } from './helpers';
+/** For seamless domains, returns the last (global fallback) layer's domain value;
+ * for regular domains, returns the domain value unchanged. */
+const getMetaDomainValue = (domainValue: string): string => {
+ const domainObj = get(selectedDomain);
+ if (domainObj && 'layers' in domainObj && domainObj.layers.length > 0) {
+ return domainObj.layers[domainObj.layers.length - 1].domainValue;
+ }
+ return domainValue;
+};
+
export const getInitialMetaData = async () => {
const domain = get(selectedDomain);
- const uri = getBaseUri(domain.value);
+ const metaDomainValue =
+ 'layers' in domain ? domain.layers[domain.layers.length - 1].domainValue : domain.value;
+ const uri = getBaseUri(metaDomainValue);
const [latestRes, inProgressRes] = await Promise.all([
- fetch(`${uri}/data_spatial/${domain.value}/latest.json`),
- fetch(`${uri}/data_spatial/${domain.value}/in-progress.json`)
+ fetch(`${uri}/data_spatial/${metaDomainValue}/latest.json`),
+ fetch(`${uri}/data_spatial/${metaDomainValue}/in-progress.json`)
]);
for (const res of [latestRes, inProgressRes]) {
@@ -51,7 +63,8 @@ const fetchMetaData = async (
export const getMetaData = async (): Promise => {
const domain = get(d);
- const uri = getBaseUri(domain);
+ const metaDomain = getMetaDomainValue(domain);
+ const uri = getBaseUri(metaDomain);
const latest = get(l);
const latestReferenceTime = toDate(latest?.reference_time);
@@ -68,7 +81,7 @@ export const getMetaData = async (): Promise => {
? (latest as DomainMetaDataJson)
: matchesModelRun(inProgressReferenceTime, modelRun)
? (inProgress as DomainMetaDataJson)
- : await fetchMetaData(uri, domain, modelRun);
+ : await fetchMetaData(uri, metaDomain, modelRun);
result.valid_times.sort();
return result;
diff --git a/src/lib/popup.ts b/src/lib/popup.ts
index 8e830ef0..447a0a31 100644
--- a/src/lib/popup.ts
+++ b/src/lib/popup.ts
@@ -1,7 +1,9 @@
import { get } from 'svelte/store';
import {
+ type Domain,
GridFactory,
+ type SeamlessDomain,
createClippingTester,
getCachedResolvedClipping,
getColor,
@@ -76,7 +78,32 @@ const updatePopupContent = async (coordinates: maplibregl.LngLat): Promise
const activeUrl = rasterManager?.getActiveSourceUrl();
if (!activeUrl) return;
- const { value } = await getValueFromLatLong(coordinates.lat, coordinates.lng, activeUrl);
+ const domain = get(selectedDomain);
+ let value: number;
+ if ('layers' in domain) {
+ // Seamless domain: try each sub-layer finest-first — states are stored
+ // under the concrete domain keys, not the seamless URL key.
+ const seamlessDomain = domain as SeamlessDomain;
+ value = NaN;
+ for (const layer of seamlessDomain.layers) {
+ const subLayerUrl = activeUrl.replace(
+ `/data_spatial/${seamlessDomain.value}/`,
+ `/data_spatial/${layer.domainValue}/`
+ );
+ try {
+ const result = await getValueFromLatLong(coordinates.lat, coordinates.lng, subLayerUrl);
+ if (isFinite(result.value)) {
+ value = result.value;
+ break;
+ }
+ } catch {
+ // Sub-layer state not found (tile not yet loaded), try next
+ }
+ }
+ } else {
+ const result = await getValueFromLatLong(coordinates.lat, coordinates.lng, activeUrl);
+ value = result.value;
+ }
if (isFinite(value)) {
const omProtocolSettingsState = get(omProtocolSettings);
@@ -116,7 +143,18 @@ const updatePopupContent = async (coordinates: maplibregl.LngLat): Promise
contentDiv.style.backgroundColor = '';
contentDiv.style.color = '';
- const domainBounds = GridFactory.create(get(selectedDomain).grid).getBounds();
+ const activeDomain = get(selectedDomain);
+ const concreteDomain: Domain =
+ 'layers' in activeDomain
+ ? (get(omProtocolSettings).domainOptions.find(
+ (d) =>
+ d.value ===
+ (activeDomain as SeamlessDomain).layers[
+ (activeDomain as SeamlessDomain).layers.length - 1
+ ].domainValue && !('layers' in d)
+ ) as Domain)
+ : (activeDomain as Domain);
+ const domainBounds = GridFactory.create(concreteDomain.grid).getBounds();
const [minLon, minLat, maxLon, maxLat] = domainBounds;
const insideDomain =
coordinates.lat >= minLat &&
diff --git a/src/lib/prefetch.ts b/src/lib/prefetch.ts
index fb2b40d5..c8697d7b 100644
--- a/src/lib/prefetch.ts
+++ b/src/lib/prefetch.ts
@@ -8,7 +8,7 @@ import { MILLISECONDS_PER_DAY } from './constants';
import { fmtModelRun, fmtSelectedTime, getBaseUri } from './helpers';
import { selectedDomain } from './stores/variables';
-import type { DomainMetaDataJson } from '@openmeteo/weather-map-layer';
+import type { Domain, DomainMetaDataJson } from '@openmeteo/weather-map-layer';
export type PrefetchMode = 'today' | 'next24h' | 'prev24h' | 'completeModelRun';
@@ -121,7 +121,7 @@ export const prefetchData = async (
try {
const instance = getProtocolInstance(get(omProtocolSettings));
- const ranges = getRanges(get(selectedDomain).grid, currentBounds);
+ const ranges = getRanges((get(selectedDomain) as Domain).grid, currentBounds);
const omFileReader = instance.omFileReader;
// Build base URL
diff --git a/src/lib/stores/om-protocol-settings.ts b/src/lib/stores/om-protocol-settings.ts
index de1a313b..1116d5af 100644
--- a/src/lib/stores/om-protocol-settings.ts
+++ b/src/lib/stores/om-protocol-settings.ts
@@ -1,10 +1,7 @@
import { type Writable, get, writable } from 'svelte/store';
import { BrowserBlockCache } from '@openmeteo/file-reader';
-import {
- type WeatherMapLayerFileReader,
- defaultOmProtocolSettings
-} from '@openmeteo/weather-map-layer';
+import { WeatherMapLayerFileReader, defaultOmProtocolSettings } from '@openmeteo/weather-map-layer';
import { persisted } from 'svelte-persisted-store';
import { browser } from '$app/environment';
diff --git a/src/lib/stores/preferences.ts b/src/lib/stores/preferences.ts
index eaedb2b9..aecc890a 100644
--- a/src/lib/stores/preferences.ts
+++ b/src/lib/stores/preferences.ts
@@ -42,6 +42,7 @@ export interface Preferences {
hillshade: boolean;
clipWater: boolean;
showScale: boolean;
+ showSeamlessBorders: boolean;
}
export const preferences = persisted('preferences', defaultPreferences);
diff --git a/src/lib/url.ts b/src/lib/url.ts
index 5a2ae91c..18c80cc5 100644
--- a/src/lib/url.ts
+++ b/src/lib/url.ts
@@ -2,10 +2,12 @@ import { tick } from 'svelte';
import { get } from 'svelte/store';
import {
+ type AnyDomain,
type Domain,
type DomainMetaDataJson,
closestModelRun,
defaultOmProtocolSettings,
+ domainOptions,
domainStep
} from '@openmeteo/weather-map-layer';
import { mode } from 'mode-watcher';
@@ -211,9 +213,19 @@ export const getOMUrl = () => {
export const getNextOmUrls = (
_omUrl: string,
- domain: Domain,
+ anyDomain: AnyDomain,
metaJson: DomainMetaDataJson | undefined
): [string | undefined, string | undefined] => {
+ // For seamless domains, resolve the last (global fallback) backing domain for
+ // URL construction and time-interval access.
+ const domain: Domain =
+ 'layers' in anyDomain
+ ? (domainOptions.find(
+ (d) =>
+ d.value === anyDomain.layers[anyDomain.layers.length - 1].domainValue &&
+ !('layers' in d)
+ ) as Domain)
+ : anyDomain;
const base = `https://map-tiles.open-meteo.com/data_spatial/${domain.value}`;
const date = get(time);
const dateString = formatISOUTCWithZ(date);
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index febc880c..4c901c58 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -3,7 +3,6 @@
import { get } from 'svelte/store';
import {
- type Domain,
GridFactory,
domainOptions,
omProtocol,
@@ -56,6 +55,7 @@
import '../styles.css';
+ import type { Domain } from '@openmeteo/weather-map-layer';
import type { RequestParameters } from 'maplibre-gl';
let clippingPanel: ReturnType;
@@ -88,17 +88,28 @@
const style = await getStyle();
- const domainObject = domainOptions.find(({ value }: Domain) => value === $domain);
+ const domainObject = domainOptions.find(({ value }) => value === $domain);
if (!domainObject) {
throw new Error('Domain not found');
}
- const grid = GridFactory.create(domainObject.grid);
+ // For seamless domains, use the global (last) backing domain for initial map position
+ const gridDomainValue =
+ 'layers' in domainObject
+ ? domainObject.layers[domainObject.layers.length - 1].domainValue
+ : domainObject.value;
+ const gridDomain = domainOptions.find(({ value }) => value === gridDomainValue) as
+ | Domain
+ | undefined;
+ if (!gridDomain) {
+ throw new Error('Backing domain not found');
+ }
+ const grid = GridFactory.create(gridDomain.grid);
$map = new maplibregl.Map({
container: mapContainer as HTMLElement,
style: style,
center: grid.getCenter(),
- zoom: domainObject.grid.zoom,
+ zoom: gridDomain.grid.zoom,
keyboard: false,
hash: true,
maxPitch: 85