ixmaps-gl 0.2.1 → 0.2.3

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 +7 -6
  2. package/ixmaps-gl.js +83 -18
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -72,7 +72,7 @@ circle/square/diamond/triangle. See the top-of-file comment in
72
72
  A page's `.require(url)` scripts (page code, e.g. a user chart function) load in order before the
73
73
  layers are built. `ixmaps.map()` is the map handle as in flat — its calls wait until the map is ready —
74
74
  with `add(theme, flags)` / `replace` / `replaceTheme` / `remove`, `changeThemeStyle` for any style key,
75
- `getZoom()` (flat's zoom), `resize()` and `setBasemapOpacity`; the page's `htmlgui_onNewTheme(id)` and
75
+ `getZoom()` (flat's zoom), `resize()`, `setBasemapOpacity` and `setMapType(id)` (also `setMapTypeId`, `mapType`: a basemap name reading dark/black/night/matter loads CARTO Dark Matter, any other name CARTO Positron, a color a plain background — the same rule the `mapType` option follows at start); the page's `htmlgui_onNewTheme(id)` and
76
76
  `htmlgui_onZoomAndPan()` (also once on load) are called (as in flat, a theme swapped in with `replace` comes last
77
77
  in the legend, while FEATURE and CHOROPLETH shapes always draw under the charts), and `map`, `getZoom` and flat's
78
78
  `formatValue` exist as globals. `.data({name})` without a URL waits for data of that name: from
@@ -155,8 +155,9 @@ ones, `replace` swaps themes by `style.name`/`meta.name`, `themeonly`/`maponly`/
155
155
  work as in flat. Themes in flat's older shape (data source in `style.dbtable*`, value field at
156
156
  the top level) are translated first. It resolves to `{ themes, skipped, notes }`: a theme
157
157
  that can't load is skipped with a warning, the rest still load. **Code a project names is not
158
- run by default** — `required` scripts, `ext` data scripts and a project's `process` functions are
159
- reported, not executed (only a page's own `.data({process})` runs) — and the basemap isn't switched.
158
+ run by default** — `required` scripts, `ext` data scripts and a project's `process` and `query`
159
+ functions and function-string `colorscheme`s are reported, not executed (only a page's own
160
+ `.data({process, query})` and colorscheme functions run) — and the basemap isn't switched.
160
161
 
161
162
  A page can opt in to a project's **scripts** (`data.ext`) for script URLs under prefixes it lists —
162
163
  no built-in prefixes, and a project file's own `options.trustedscripts` is ignored. Both of flat's
@@ -192,7 +193,7 @@ of your own project files gl can render.
192
193
  Load the engine from jsDelivr, pinned to a release tag (or `@main` for the latest commit):
193
194
 
194
195
  ```html
195
- <script src="https://cdn.jsdelivr.net/gh/gjrichter/ixmaps-gl@v0.2.1/ixmaps-gl.js"></script>
196
+ <script src="https://cdn.jsdelivr.net/gh/gjrichter/ixmaps-gl@v0.2.3/ixmaps-gl.js"></script>
196
197
  ```
197
198
 
198
199
  or from unpkg, the npm package [`ixmaps-gl`](https://www.npmjs.com/package/ixmaps-gl)
@@ -200,13 +201,13 @@ or from unpkg, the npm package [`ixmaps-gl`](https://www.npmjs.com/package/ixmap
200
201
  browser script, which defines the global `ixmaps`):
201
202
 
202
203
  ```html
203
- <script src="https://unpkg.com/ixmaps-gl@0.2.1/ixmaps-gl.js"></script>
204
+ <script src="https://unpkg.com/ixmaps-gl@0.2.3/ixmaps-gl.js"></script>
204
205
  ```
205
206
 
206
207
  or use a local copy (`<script src="ixmaps-gl.js"></script>`). A minimal page:
207
208
 
208
209
  ```html
209
- <script src="https://cdn.jsdelivr.net/gh/gjrichter/ixmaps-gl@v0.2.1/ixmaps-gl.js"></script>
210
+ <script src="https://cdn.jsdelivr.net/gh/gjrichter/ixmaps-gl@v0.2.3/ixmaps-gl.js"></script>
210
211
  <div id="map_div" style="position:absolute;inset:0;"></div>
211
212
 
212
213
  <script>
package/ixmaps-gl.js CHANGED
@@ -80,7 +80,7 @@
80
80
  // ---------------------------------------------------------------
81
81
  // this engine's release (= package.json "version", checked by
82
82
  // test/unit/version.test.mjs); ixmaps.glVersion, and logged once at start
83
- const IXMAPS_GL_VERSION = '0.2.1';
83
+ const IXMAPS_GL_VERSION = '0.2.3';
84
84
 
85
85
  const LIB_URLS = {
86
86
  // Bumped 3.6.2 -> 5.x (2026-09-21, globe-projection compat fix): native
@@ -1720,7 +1720,7 @@
1720
1720
  // htmlgui_flat.js). Its methods act on the last built map; called while
1721
1721
  // the map is still building, they run once it is ready.
1722
1722
  const MAP_HANDLE_METHODS = ['replace', 'add', 'remove', 'removeTheme', 'replaceTheme', 'changeThemeStyle', 'setThemeStyle',
1723
- 'refreshTheme', 'setBasemapOpacity', 'resize', 'view', 'options', 'layer', 'loadProject', 'require', 'setThemeVisible'];
1723
+ 'refreshTheme', 'setBasemapOpacity', 'setMapType', 'setMapTypeId', 'mapType', 'resize', 'view', 'options', 'layer', 'loadProject', 'require', 'setThemeVisible'];
1724
1724
  const _mapHandle = {};
1725
1725
  for (const m of MAP_HANDLE_METHODS) {
1726
1726
  _mapHandle[m] = (...args) => {
@@ -2148,6 +2148,36 @@
2148
2148
  dbtable: 'name', dbtableUrl: 'url', dbtableType: 'type', dbtableExt: 'ext',
2149
2149
  dbtableProcess: 'process', dbtableQuery: 'query', dbtableObj: 'obj', datacache: 'cache',
2150
2150
  };
2151
+ // Every project key the engine would turn into code (new Function)
2152
+ // is kept out, never run — a page's own .data({process, query}) and
2153
+ // .style({colorscheme: fn.toString()}) still run. Project keys of
2154
+ // that kind: the data processing function (data.process /
2155
+ // style.dbtableProcess), the broker query (data.query /
2156
+ // style.dbtableQuery — without it a data.name still reaches a
2157
+ // provider the PAGE defined as ixmaps[name]) and a colorscheme given
2158
+ // as a string, which resolveColorScheme evaluates as a function
2159
+ // (an array colorscheme is data and stays).
2160
+ const PROJECT_CODE_KEYS = [
2161
+ ['data', 'process', 'data.process function'],
2162
+ ['style', 'dbtableProcess', 'data.process function'],
2163
+ ['data', 'query', 'data.query function'],
2164
+ ['style', 'dbtableQuery', 'data.query function'],
2165
+ ['style', 'colorscheme', 'colorscheme function', v => typeof v === 'string'],
2166
+ ];
2167
+ function withoutProjectCode(t, report) {
2168
+ const found = PROJECT_CODE_KEYS.filter(([part, key, , isCode]) =>
2169
+ t[part] && t[part][key] != null && (!isCode || isCode(t[part][key])));
2170
+ if (!found.length) return t;
2171
+ const copy = Object.assign({}, t);
2172
+ found.forEach(([part, key]) => {
2173
+ if (copy[part] === t[part]) copy[part] = Object.assign({}, t[part]);
2174
+ delete copy[part][key];
2175
+ });
2176
+ const what = [...new Set(found.map(f => f[2]))].join(', ');
2177
+ report.notes.push(`theme "${t.layer}": the project's ${what} is never run by ixmaps-gl`);
2178
+ return copy;
2179
+ }
2180
+
2151
2181
  function projectThemeToDefinition(theme) {
2152
2182
  const t = theme || {};
2153
2183
  const style = Object.assign({}, t.style);
@@ -2418,6 +2448,19 @@
2418
2448
  const named = NAMED_MAPTYPE_COLORS[t.toLowerCase()];
2419
2449
  return named || null;
2420
2450
  }
2451
+ // A basemap NAME (flat's mapType: "VT_TONER_LITE", "VT_DATAVIZ_DARK",
2452
+ // "CartoDB - Dark matter", ...) picks one of two keyless CARTO vector
2453
+ // styles: dark for a name reading dark/black/night/matter, otherwise
2454
+ // light (Positron, gl's default). A color mapType (resolveMapTypeColor)
2455
+ // is a plain background instead and wins, so "dark" alone stays the
2456
+ // #1a1a1a background it always was.
2457
+ const BASEMAP_STYLE_URLS = {
2458
+ light: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
2459
+ dark: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
2460
+ };
2461
+ function resolveBasemapStyleUrl(mapType) {
2462
+ return typeof mapType === 'string' && /dark|black|night|matter/i.test(mapType) ? BASEMAP_STYLE_URLS.dark : BASEMAP_STYLE_URLS.light;
2463
+ }
2421
2464
  // Real ixmaps-flat's legend look (tools/legend.js 3773-3845, via
2422
2465
  // tools/background_theme.js): DARK when the basemap is more than half
2423
2466
  // opaque and the map background reads as dark — a map type naming
@@ -2688,7 +2731,7 @@
2688
2731
  const map = new maplibregl.Map({
2689
2732
  container: this.containerId,
2690
2733
  style: mapTypeColor ? buildBlankBackgroundStyle(mapTypeColor)
2691
- : 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
2734
+ : resolveBasemapStyleUrl(this.mapOptions.mapType),
2692
2735
  center: [lon, lat],
2693
2736
  zoom: this._viewZoom != null && this._viewZoom !== '' ? flatToMapLibreZoom(Number(this._viewZoom)) : 8,
2694
2737
  // .attribution(a) (MapBuilder, above) was stored but never read —
@@ -3140,6 +3183,31 @@
3140
3183
  },
3141
3184
  replaceTheme: (id, layerBuilder, flag) => engineApi.replace(id, layerBuilder, flag),
3142
3185
  remove: (id) => { removeThemeById(id); return engineApi; },
3186
+ // flat's map.setMapType(id) / setMapTypeId / mapType (htmlgui_flat.js
3187
+ // mapApi): swap the basemap at runtime. MapLibre's setStyle replaces
3188
+ // the whole style; deck.gl's interleaved overlay re-adds its own
3189
+ // layers on 'styledata' by itself. What the new style resets is put
3190
+ // back once it has loaded: the projection (globe), the basemap
3191
+ // opacity (it is written into the style's paint properties).
3192
+ setMapType: (id) => {
3193
+ const color = resolveMapTypeColor(id);
3194
+ const style = color ? buildBlankBackgroundStyle(color) : resolveBasemapStyleUrl(id);
3195
+ const current = color ? null : resolveBasemapStyleUrl(builder.mapOptions.mapType);
3196
+ const wasColor = !!resolveMapTypeColor(builder.mapOptions.mapType);
3197
+ builder.mapOptions.mapType = id;
3198
+ if (!color && !wasColor && style === current) return engineApi; // same style: nothing to reload
3199
+ const projection = typeof map.getProjection === 'function' ? map.getProjection() : null;
3200
+ map.once('style.load', () => {
3201
+ if (projection && projection.type && typeof map.setProjection === 'function') {
3202
+ try { map.setProjection({ type: projection.type }); } catch (e) { /* projection unsupported by this build */ }
3203
+ }
3204
+ applyBasemapOpacity();
3205
+ });
3206
+ map.setStyle(style);
3207
+ return engineApi;
3208
+ },
3209
+ setMapTypeId: (id) => engineApi.setMapType(id),
3210
+ mapType: (id) => engineApi.setMapType(id),
3143
3211
  setBasemapOpacity: (delta, mode) => {
3144
3212
  _basemapOpacity = mode === 'relative'
3145
3213
  ? Math.max(0, Math.min(1, _basemapOpacity + (parseFloat(delta) || 0)))
@@ -3611,19 +3679,6 @@
3611
3679
  return report;
3612
3680
  }
3613
3681
 
3614
- // a project file's data processing function is code the project
3615
- // names — kept out, never run (a page's own .data({process}) runs)
3616
- function withoutProjectCode(t, report) {
3617
- const hasData = t.data && t.data.process != null;
3618
- const hasStyle = t.style && t.style.dbtableProcess != null;
3619
- if (!hasData && !hasStyle) return t;
3620
- report.notes.push(`theme "${t.layer}": the project's data.process function is never run by ixmaps-gl`);
3621
- const copy = Object.assign({}, t);
3622
- if (hasData) { copy.data = Object.assign({}, t.data); delete copy.data.process; }
3623
- if (hasStyle) { copy.style = Object.assign({}, t.style); delete copy.style.dbtableProcess; }
3624
- return copy;
3625
- }
3626
-
3627
3682
  function applyProjectMap(m, f, report) {
3628
3683
  const svg = String(m.map || '');
3629
3684
  if (svg && !/generic\/(mercator|orthographic)\.svg$/i.test(svg)) report.notes.push(`flat SVG map "${svg.split('/').pop()}" is not available in ixmaps-gl — mercator used`);
@@ -5266,6 +5321,16 @@
5266
5321
  return text;
5267
5322
  }
5268
5323
 
5324
+ // Records behind one drawn chart item: a group's per-category record
5325
+ // counts summed, else a Supercluster cluster's point_count, else 1.
5326
+ // recordCounts only exists on RELOCATE groups (groupCoLocated); a plain
5327
+ // grid group (no RELOCATE) carries counts only — read unguarded, BOX|TITLE
5328
+ // without RELOCATE threw on the first cluster and the theme stopped drawing.
5329
+ function groupRecordCount(props) {
5330
+ const perCat = props.recordCounts || props.counts;
5331
+ return perCat ? perCat.reduce((x, c) => x + c, 0) : (props.point_count || 1);
5332
+ }
5333
+
5269
5334
  function valuesFontSizePx(radiusPx, text, valueScale) {
5270
5335
  return Math.min(radiusPx * 0.8, radiusPx * (3.3 / Math.max(1, text.length))) * (valueScale || 1);
5271
5336
  }
@@ -9364,7 +9429,7 @@
9364
9429
  let title = null;
9365
9430
  if (titleShown) {
9366
9431
  const raw = d.properties.raw;
9367
- const n = d.properties.counts ? d.properties.recordCounts.reduce((x, c) => x + c, 0) : (d.properties.point_count || 1);
9432
+ const n = groupRecordCount(d.properties);
9368
9433
  const text = n > 3 ? '(' + n + ')' : (raw && raw[this.binding.title] != null ? String(raw[this.binding.title]) : '');
9369
9434
  if (text) {
9370
9435
  const f = titleFontPx;
@@ -9987,7 +10052,7 @@
9987
10052
  // engine runs in a Node vm there); deliberately NOT on the ixmaps object
9988
10053
  global.__ixmapsGlInternals = {
9989
10054
  IXMAPS_GL_VERSION,
9990
- normalizeTheme, projectThemeToDefinition, LayerBuilder, LayerRuntime, typeStyleNumbers, styleNum,
10055
+ normalizeTheme, projectThemeToDefinition, withoutProjectCode, groupRecordCount, resolveBasemapStyleUrl, resolveMapTypeColor, LayerBuilder, LayerRuntime, typeStyleNumbers, styleNum,
9991
10056
  resolveScriptUrl, isTrustedScriptUrl, loadProcessingScript, loadBrokerData, applyBrokerThemePatch, makeBrokerTheme,
9992
10057
  equalIntervalBreaks, quantileBreaks, naturalBreaks, evenStrideSample, jenksBreakValues, partsFromBreakValues, resolvePartsClass,
9993
10058
  computeAlphaStats, computeMultiFieldClasses, computeDominantStats, resolveDominantClass, computeComposeColorStats,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ixmaps-gl",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
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",