ixmaps-gl 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +10 -2
  2. package/ixmaps-gl.js +67 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -190,13 +190,21 @@ of your own project files gl can render.
190
190
  Load the engine from jsDelivr, pinned to a release tag (or `@main` for the latest commit):
191
191
 
192
192
  ```html
193
- <script src="https://cdn.jsdelivr.net/gh/gjrichter/ixmaps-gl@v0.1.0/ixmaps-gl.js"></script>
193
+ <script src="https://cdn.jsdelivr.net/gh/gjrichter/ixmaps-gl@v0.2.0/ixmaps-gl.js"></script>
194
+ ```
195
+
196
+ or from unpkg, the npm package [`ixmaps-gl`](https://www.npmjs.com/package/ixmaps-gl)
197
+ (`https://unpkg.com/ixmaps-gl` for the latest version; `npm install ixmaps-gl` installs the same
198
+ browser script, which defines the global `ixmaps`):
199
+
200
+ ```html
201
+ <script src="https://unpkg.com/ixmaps-gl@0.2.0/ixmaps-gl.js"></script>
194
202
  ```
195
203
 
196
204
  or use a local copy (`<script src="ixmaps-gl.js"></script>`). A minimal page:
197
205
 
198
206
  ```html
199
- <script src="https://cdn.jsdelivr.net/gh/gjrichter/ixmaps-gl@v0.1.0/ixmaps-gl.js"></script>
207
+ <script src="https://cdn.jsdelivr.net/gh/gjrichter/ixmaps-gl@v0.2.0/ixmaps-gl.js"></script>
200
208
  <div id="map_div" style="position:absolute;inset:0;"></div>
201
209
 
202
210
  <script>
package/ixmaps-gl.js CHANGED
@@ -78,14 +78,27 @@
78
78
  // Same CDN builds every example page already pinned; centralized here
79
79
  // so bumping a version is a one-file edit, not a 13-file one.
80
80
  // ---------------------------------------------------------------
81
+ // this engine's release (= package.json "version", checked by
82
+ // test/unit/version.test.mjs); ixmaps.glVersion, and logged once at start
83
+ const IXMAPS_GL_VERSION = '0.2.0';
84
+
81
85
  const LIB_URLS = {
82
86
  // Bumped 3.6.2 -> 5.x (2026-09-21, globe-projection compat fix): native
83
87
  // globe projection (map.setProjection({type:'globe'})) needs >=5.0.1
84
88
  // (5.0.0 shipped a style-spec regression, reverted in 5.0.1). Requires
85
89
  // MapLibre GL JS v4.5.1, v5, or v6 on the deck.gl side (see `deck`
86
90
  // below) — v5 satisfies that.
87
- maplibreCss: 'https://unpkg.com/maplibre-gl@5/dist/maplibre-gl.css',
88
- maplibreJs: 'https://unpkg.com/maplibre-gl@5/dist/maplibre-gl.js',
91
+ // 5.x -> 6.x (2026-09-29): MapLibre 6 is published only as an ES
92
+ // module (dist/maplibre-gl.mjs, named exports, no UMD bundle and no
93
+ // window.maplibregl) — loadMaplibre imports it and provides the global.
94
+ // Its worker (maplibre-gl-worker.mjs, resolved next to the module) is
95
+ // cross-origin from the CDN; MapLibre wraps it in a same-origin blob URL
96
+ // itself. Pinned to an exact version: unpkg answers a version range
97
+ // (@6) with a redirect, and a browser refuses a worker script that
98
+ // redirects ("Refused to cross-origin redirects of the top-level worker
99
+ // script" — the map never loaded on a file:// page).
100
+ maplibreCss: 'https://unpkg.com/maplibre-gl@6.11.2/dist/maplibre-gl.css',
101
+ maplibreJs: 'https://unpkg.com/maplibre-gl@6.11.2/dist/maplibre-gl.mjs',
89
102
  // Bumped 8.9.35 -> 9.4.0 (2026-09-21, globe-reprojection fix): v8's
90
103
  // interleaving (MapboxOverlay) always computed flat Web-Mercator
91
104
  // screen positions regardless of the map's actual projection —
@@ -117,6 +130,49 @@
117
130
  document.head.appendChild(el);
118
131
  });
119
132
  }
133
+ // MapLibre 6's ES module → the window.maplibregl the engine and pages use
134
+ // (a copy of its named exports: a module namespace is read-only)
135
+ function loadMaplibre(src) {
136
+ installFileWorkerShim();
137
+ return import(src).then(mod => {
138
+ global.maplibregl = Object.assign({}, mod.default || {}, mod);
139
+ }, err => { throw new Error(`[ixmaps-gl] failed to load ${src}: ${err && err.message}`); });
140
+ }
141
+ // MapLibre 6 starts its worker as a module worker; for a worker on the
142
+ // CDN it wraps it in a blob URL (`import "…maplibre-gl-worker.mjs"`).
143
+ // On a file:// page Chrome refuses every module worker from a blob URL
144
+ // ("Refused to cross-origin redirects of the top-level worker script" —
145
+ // the map never loaded), while a module worker from a data: URL works.
146
+ // Only there, and only for MapLibre's own wrapper, the Worker is started
147
+ // from the same import as a data: URL. Other workers are untouched.
148
+ function installFileWorkerShim() {
149
+ if (!global.location || global.location.protocol !== 'file:' || !global.Worker || global.Worker.__ixmapsGlFileShim) return;
150
+ const NativeBlob = global.Blob, NativeWorker = global.Worker;
151
+ const maplibreWorkerImport = new WeakMap(); // Blob → its import statement
152
+ const blobUrls = new Map(); // blob: URL → import statement
153
+ const isMaplibreImport = t => typeof t === 'string' && /^import "https?:[^"]*\/maplibre-gl-worker(-dev)?\.mjs"$/.test(t);
154
+ function Blob(parts, opts) {
155
+ const b = new NativeBlob(parts, opts);
156
+ if (Array.isArray(parts) && parts.length === 1 && isMaplibreImport(parts[0])) maplibreWorkerImport.set(b, parts[0]);
157
+ return b;
158
+ }
159
+ Blob.prototype = NativeBlob.prototype;
160
+ global.Blob = Blob;
161
+ const nativeCreate = global.URL.createObjectURL;
162
+ global.URL.createObjectURL = function (obj) {
163
+ const u = nativeCreate.call(this, obj);
164
+ if (maplibreWorkerImport.has(obj)) blobUrls.set(u, maplibreWorkerImport.get(obj));
165
+ return u;
166
+ };
167
+ function Worker(url, opts) {
168
+ const imp = blobUrls.get(String(url));
169
+ if (imp && opts && opts.type === 'module') return new NativeWorker('data:text/javascript,' + encodeURIComponent(imp), opts);
170
+ return new NativeWorker(url, opts);
171
+ }
172
+ Worker.prototype = NativeWorker.prototype;
173
+ Worker.__ixmapsGlFileShim = true;
174
+ global.Worker = Worker;
175
+ }
120
176
  function loadStylesheet(href) {
121
177
  return new Promise((resolve, reject) => {
122
178
  const el = document.createElement('link');
@@ -188,7 +244,7 @@
188
244
  }
189
245
  if (i === 2) return true;
190
246
  }
191
- console.warn(`[ixmaps-gl] this page loads maplibre-gl ${v || '(unknown version)'}; deck.gl needs >= ${MAPLIBRE_MIN_VERSION.join('.')} — loading ${LIB_URLS.maplibreJs.match(/maplibre-gl@([\d.]+)/)[1]}.x instead (it replaces window.maplibregl); remove the page's own maplibre-gl <script> tag`);
247
+ console.warn(`[ixmaps-gl] this page loads maplibre-gl ${v || '(unknown version)'}; deck.gl needs >= ${MAPLIBRE_MIN_VERSION.join('.')} — loading ${LIB_URLS.maplibreJs.match(/maplibre-gl@([\d.]+)/)[1]} instead (it replaces window.maplibregl); remove the page's own maplibre-gl <script> tag`);
192
248
  return false;
193
249
  }
194
250
  // data.js, unless the page has it already: .options({datajs: url}) loads
@@ -213,12 +269,16 @@
213
269
  function ensureLibrariesLoaded() {
214
270
  if (!_librariesPromise) {
215
271
  _librariesPromise = Promise.all([
216
- pageMaplibreUsable() ? Promise.resolve() : loadScript(LIB_URLS.maplibreJs),
272
+ pageMaplibreUsable() ? Promise.resolve() : loadMaplibre(LIB_URLS.maplibreJs),
217
273
  pageDeckUsable() ? Promise.resolve() : loadScript(LIB_URLS.deck),
218
274
  global.Mustache ? Promise.resolve() : loadScript(LIB_URLS.mustache),
219
275
  [...document.styleSheets].some(s => s.href === LIB_URLS.maplibreCss) ? Promise.resolve() : loadStylesheet(LIB_URLS.maplibreCss)
220
276
  ]).then(() => {
221
277
  ({ IconLayer, ScatterplotLayer, GeoJsonLayer, MapLibreOverlay, TextLayer } = global.deck);
278
+ // which engine and libraries this page really runs (a page may
279
+ // bring its own MapLibre/deck.gl — the loaded ones are named)
280
+ const ml = global.maplibregl && typeof global.maplibregl.getVersion === 'function' ? global.maplibregl.getVersion() : '?';
281
+ console.info(`[ixmaps-gl] ${IXMAPS_GL_VERSION} · MapLibre ${ml} · deck.gl ${global.deck.VERSION || '?'}`);
222
282
  // Matches the real engine's own ui/js/tools/tooltip_mustache.js,
223
283
  // which overrides Mustache.escape to identity: tooltip HTML (the
224
284
  // template itself, and this engine's own chart/data-table
@@ -9887,6 +9947,8 @@
9887
9947
  szResourceBase: ixmapsSzResourceBase, getProjectString, setProjectJSON, loadProject,
9888
9948
  markThemeClass, unmarkThemeClass, getBoundingBox, setTitle, setTitleBox, refreshTheme, formatValue: flatFormatValue,
9889
9949
  map: mapHandle, getZoom,
9950
+ // this engine's version (flat's own ixmaps.version numbers flat)
9951
+ glVersion: IXMAPS_GL_VERSION,
9890
9952
  // flat always has this page hook (its layer legend tool defines it,
9891
9953
  // ui/js/tools/layer_legend.js 702-712): pages wrap it and call the one
9892
9954
  // they replaced — the chain ends here
@@ -9905,6 +9967,7 @@
9905
9967
  // test-only: lets test/unit/*.test.mjs call pure internals directly (the
9906
9968
  // engine runs in a Node vm there); deliberately NOT on the ixmaps object
9907
9969
  global.__ixmapsGlInternals = {
9970
+ IXMAPS_GL_VERSION,
9908
9971
  normalizeTheme, projectThemeToDefinition, LayerBuilder, LayerRuntime, typeStyleNumbers, styleNum,
9909
9972
  resolveScriptUrl, isTrustedScriptUrl, loadProcessingScript, loadBrokerData, applyBrokerThemePatch, makeBrokerTheme,
9910
9973
  equalIntervalBreaks, quantileBreaks, naturalBreaks, evenStrideSample, jenksBreakValues, partsFromBreakValues, resolvePartsClass,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ixmaps-gl",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "MapLibre GL + deck.gl reimplementation of the ixmaps-flat declarative mapping engine",
5
5
  "main": "ixmaps-gl.js",
6
6
  "browser": "ixmaps-gl.js",