@mwater/visualization 5.6.0 → 5.6.1

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 (92) hide show
  1. package/lib/ColorComponent.js +2 -2
  2. package/lib/TranslationsTabComponent.d.ts +34 -0
  3. package/lib/TranslationsTabComponent.js +256 -0
  4. package/lib/dashboards/DashboardComponent.js +1 -1
  5. package/lib/dashboards/ServerDashboardDataSource.d.ts +0 -1
  6. package/lib/dashboards/ServerDashboardDataSource.js +0 -15
  7. package/lib/dashboards/SettingsModalComponent.js +9 -233
  8. package/lib/datagrids/DatagridComponent.js +5 -0
  9. package/lib/datagrids/DatagridViewComponent.js +30 -4
  10. package/lib/maps/BufferLayer.d.ts +0 -13
  11. package/lib/maps/BufferLayer.js +12 -237
  12. package/lib/maps/BufferLayerDesignerComponent.d.ts +1 -1
  13. package/lib/maps/BufferLayerDesignerComponent.js +0 -5
  14. package/lib/maps/ChoroplethLayer.d.ts +1 -16
  15. package/lib/maps/ChoroplethLayer.js +13 -358
  16. package/lib/maps/ClusterLayer.d.ts +0 -9
  17. package/lib/maps/ClusterLayer.js +0 -250
  18. package/lib/maps/DirectMapDataSource.js +1 -38
  19. package/lib/maps/GridLayer.d.ts +0 -15
  20. package/lib/maps/GridLayer.js +0 -212
  21. package/lib/maps/Layer.d.ts +1 -26
  22. package/lib/maps/Layer.js +0 -13
  23. package/lib/maps/MapComponent.d.ts +19 -35
  24. package/lib/maps/MapComponent.js +135 -76
  25. package/lib/maps/MapControlComponent.d.ts +4 -5
  26. package/lib/maps/MapControlComponent.js +5 -12
  27. package/lib/maps/MapDesign.d.ts +8 -0
  28. package/lib/maps/MapDesignerComponent.d.ts +2 -0
  29. package/lib/maps/MapDesignerComponent.js +7 -2
  30. package/lib/maps/MapLayerDataSource.d.ts +0 -4
  31. package/lib/maps/MapLayerViewDesignerComponent.d.ts +3 -1
  32. package/lib/maps/MapLayerViewDesignerComponent.js +5 -1
  33. package/lib/maps/MapLayersDesignerComponent.d.ts +2 -0
  34. package/lib/maps/MapLayersDesignerComponent.js +2 -1
  35. package/lib/maps/MapTranslationsTab.d.ts +15 -0
  36. package/lib/maps/MapTranslationsTab.js +47 -0
  37. package/lib/maps/MapUtils.d.ts +11 -0
  38. package/lib/maps/MapUtils.js +47 -0
  39. package/lib/maps/MapViewComponent.d.ts +1 -1
  40. package/lib/maps/MapViewComponent.js +1 -8
  41. package/lib/maps/MarkersLayer.d.ts +1 -14
  42. package/lib/maps/MarkersLayer.js +71 -252
  43. package/lib/maps/MarkersLayerDesign.d.ts +4 -0
  44. package/lib/maps/MarkersLayerDesignerComponent.d.ts +20 -16
  45. package/lib/maps/MarkersLayerDesignerComponent.js +77 -23
  46. package/lib/maps/ServerMapDataSource.d.ts +0 -1
  47. package/lib/maps/ServerMapDataSource.js +0 -15
  48. package/lib/maps/SwitchableTileUrlLayer.d.ts +0 -2
  49. package/lib/maps/SwitchableTileUrlLayer.js +0 -9
  50. package/lib/maps/TileUrlLayer.d.ts +0 -1
  51. package/lib/maps/TileUrlLayer.js +0 -5
  52. package/lib/maps/VectorMapViewComponent.js +12 -1
  53. package/lib/maps/vectorMaps.d.ts +5 -6
  54. package/lib/maps/vectorMaps.js +13 -9
  55. package/lib/widgets/MapWidget.js +2 -1
  56. package/package.json +2 -2
  57. package/src/ColorComponent.tsx +2 -2
  58. package/src/TranslationsTabComponent.tsx +429 -0
  59. package/src/dashboards/DashboardComponent.tsx +1 -1
  60. package/src/dashboards/ServerDashboardDataSource.ts +0 -19
  61. package/src/dashboards/SettingsModalComponent.tsx +27 -383
  62. package/src/datagrids/DatagridComponent.tsx +6 -0
  63. package/src/datagrids/DatagridViewComponent.tsx +41 -5
  64. package/src/maps/BufferLayer.ts +16 -262
  65. package/src/maps/BufferLayerDesignerComponent.tsx +0 -6
  66. package/src/maps/ChoroplethLayer.ts +16 -393
  67. package/src/maps/ClusterLayer.ts +0 -274
  68. package/src/maps/DirectMapDataSource.ts +2 -49
  69. package/src/maps/GridLayer.ts +0 -224
  70. package/src/maps/Layer.ts +1 -35
  71. package/src/maps/MapComponent.tsx +448 -0
  72. package/src/maps/MapControlComponent.tsx +41 -0
  73. package/src/maps/MapDesign.ts +6 -0
  74. package/src/maps/MapDesignerComponent.tsx +18 -1
  75. package/src/maps/MapLayerDataSource.ts +0 -5
  76. package/src/maps/MapLayerViewDesignerComponent.ts +9 -2
  77. package/src/maps/MapLayersDesignerComponent.ts +4 -1
  78. package/src/maps/MapTranslationsTab.tsx +53 -0
  79. package/src/maps/MapUtils.ts +48 -0
  80. package/src/maps/MapViewComponent.tsx +2 -8
  81. package/src/maps/MarkersLayer.ts +79 -270
  82. package/src/maps/MarkersLayerDesign.ts +6 -0
  83. package/src/maps/MarkersLayerDesignerComponent.tsx +114 -38
  84. package/src/maps/ServerMapDataSource.ts +0 -19
  85. package/src/maps/SwitchableTileUrlLayer.tsx +0 -11
  86. package/src/maps/TileUrlLayer.tsx +0 -6
  87. package/src/maps/VectorMapViewComponent.tsx +13 -2
  88. package/src/maps/vectorMaps.tsx +12 -9
  89. package/src/widgets/MapWidget.tsx +2 -0
  90. package/src/maps/MapComponent.ts +0 -311
  91. package/src/maps/MapControlComponent.ts +0 -46
  92. package/src/maps/RasterMapViewComponent.ts +0 -345
@@ -3,26 +3,13 @@ import Layer, { OnGridClickOptions, OnGridHoverOptions, VectorTileDef, LegendOpt
3
3
  import { Schema, DataSource } from "@mwater/expressions";
4
4
  import { OnGridClickResults, OnGridHoverResults } from "./maps";
5
5
  import { JsonQLFilter } from "../index";
6
- import { JsonQLQuery, JsonQLSelectQuery } from "@mwater/jsonql";
6
+ import { JsonQLSelectQuery } from "@mwater/jsonql";
7
7
  import { MarkersLayerDesign } from "./MarkersLayerDesign";
8
8
  export default class MarkersLayer extends Layer<MarkersLayerDesign> {
9
9
  /** Gets the type of layer definition */
10
10
  getLayerDefinitionType(): "VectorTile";
11
11
  getVectorTile(design: MarkersLayerDesign, sourceId: string, schema: Schema, filters: JsonQLFilter[], opacity: number): VectorTileDef;
12
12
  createJsonQL(design: MarkersLayerDesign, schema: Schema, filters: JsonQLFilter[]): JsonQLSelectQuery;
13
- getJsonQLCss(design: MarkersLayerDesign, schema: Schema, filters: JsonQLFilter[]): {
14
- layers: {
15
- id: string;
16
- jsonql: JsonQLQuery;
17
- }[];
18
- css: string;
19
- interactivity: {
20
- layer: string;
21
- fields: string[];
22
- };
23
- };
24
- createMapnikJsonQL(design: MarkersLayerDesign, schema: Schema, filters: JsonQLFilter[]): JsonQLQuery;
25
- createCss(design: MarkersLayerDesign): string;
26
13
  onGridHoverOver(ev: {
27
14
  data: any;
28
15
  event: any;
@@ -142,6 +142,51 @@ class MarkersLayer extends Layer_1.default {
142
142
  filter: addFilter(["==", ["geometry-type"], "Point"])
143
143
  });
144
144
  }
145
+ // Add labels layer if label axis is defined (points only)
146
+ if (design.axes.label) {
147
+ // Determine text-anchor and text-offset based on labelPosition
148
+ let textAnchor;
149
+ let textOffset;
150
+ switch (design.labelPosition) {
151
+ case "top":
152
+ textAnchor = "bottom";
153
+ textOffset = [0, -0.8];
154
+ break;
155
+ case "left":
156
+ textAnchor = "right";
157
+ textOffset = [-0.8, 0];
158
+ break;
159
+ case "right":
160
+ textAnchor = "left";
161
+ textOffset = [0.8, 0];
162
+ break;
163
+ case "bottom":
164
+ default:
165
+ textAnchor = "top";
166
+ textOffset = [0, 0.8];
167
+ break;
168
+ }
169
+ mapLayers.push({
170
+ id: `${sourceId}:labels`,
171
+ type: "symbol",
172
+ source: sourceId,
173
+ "source-layer": "main",
174
+ layout: {
175
+ "text-field": ["to-string", ["get", "label"]],
176
+ "text-size": 10,
177
+ "text-anchor": textAnchor,
178
+ "text-offset": textOffset,
179
+ "text-allow-overlap": false
180
+ },
181
+ paint: {
182
+ "text-color": (0, mapboxUtils_1.compileColorToMapbox)("#000000", design.axes.color?.excludedValues),
183
+ "text-halo-color": (0, mapboxUtils_1.compileColorToMapbox)("rgba(255, 255, 255, 0.8)", design.axes.color?.excludedValues),
184
+ "text-halo-width": 1.5,
185
+ "text-opacity": opacity
186
+ },
187
+ filter: addFilter(["==", ["geometry-type"], "Point"])
188
+ });
189
+ }
145
190
  return {
146
191
  sourceLayers: [{ id: "main", jsonql }],
147
192
  ctes: [],
@@ -179,6 +224,11 @@ class MarkersLayer extends Layer_1.default {
179
224
  const colorExpr = axisBuilder.compileAxis({ axis: design.axes.color, tableAlias: "basequery" });
180
225
  basequery.selects.push({ type: "select", expr: colorExpr, alias: "color" });
181
226
  }
227
+ // Add label select if label axis
228
+ if (design.axes.label) {
229
+ const labelExpr = axisBuilder.compileAxis({ axis: design.axes.label, tableAlias: "basequery" });
230
+ basequery.selects.push({ type: "select", expr: labelExpr, alias: "label" });
231
+ }
182
232
  // Create filters
183
233
  let whereClauses = [];
184
234
  // Add filters baked into layer
@@ -202,252 +252,6 @@ class MarkersLayer extends Layer_1.default {
202
252
  const markersQuery = createMarkersVectorQuery(basequery);
203
253
  return markersQuery;
204
254
  }
205
- // Gets the layer definition as JsonQL + CSS in format:
206
- // {
207
- // layers: array of { id: layer id, jsonql: jsonql that includes "the_webmercator_geom" as a column }
208
- // css: carto css
209
- // interactivity: (optional) { layer: id of layer, fields: array of field names }
210
- // }
211
- // arguments:
212
- // design: design of layer
213
- // schema: schema to use
214
- // filters: array of filters to apply. Each is { table: table id, jsonql: jsonql condition with {alias} for tableAlias. Use injectAlias to put in table alias
215
- getJsonQLCss(design, schema, filters) {
216
- // Create design
217
- const layerDef = {
218
- layers: [
219
- {
220
- id: "layer0",
221
- jsonql: this.createMapnikJsonQL(design, schema, filters)
222
- }
223
- ],
224
- css: this.createCss(design),
225
- interactivity: {
226
- layer: "layer0",
227
- fields: ["id"]
228
- }
229
- };
230
- return layerDef;
231
- }
232
- createMapnikJsonQL(design, schema, filters) {
233
- const axisBuilder = new AxisBuilder_1.default({ schema });
234
- const exprCompiler = new expressions_1.ExprCompiler(schema);
235
- // Compile geometry axis
236
- let geometryExpr = axisBuilder.compileAxis({ axis: design.axes.geometry, tableAlias: "innerquery" });
237
- // row_number() over (partition by round(ST_XMin(location)/!(pixel_width!*5)), round(ST_YMin(location)/(!pixel_height!*5))) AS r
238
- const cluster = {
239
- type: "select",
240
- expr: {
241
- type: "op",
242
- op: "row_number",
243
- exprs: [],
244
- over: {
245
- partitionBy: [
246
- {
247
- type: "op",
248
- op: "round",
249
- exprs: [
250
- {
251
- type: "op",
252
- op: "/",
253
- exprs: [
254
- { type: "op", op: "ST_XMin", exprs: [geometryExpr] },
255
- { type: "op", op: "*", exprs: [{ type: "token", token: "!pixel_width!" }, 5] }
256
- ]
257
- }
258
- ]
259
- },
260
- {
261
- type: "op",
262
- op: "round",
263
- exprs: [
264
- {
265
- type: "op",
266
- op: "/",
267
- exprs: [
268
- { type: "op", op: "ST_YMin", exprs: [geometryExpr] },
269
- { type: "op", op: "*", exprs: [{ type: "token", token: "!pixel_height!" }, 5] }
270
- ]
271
- }
272
- ]
273
- }
274
- ]
275
- }
276
- },
277
- alias: "r"
278
- };
279
- // Select _id, location and clustered row number
280
- const innerquery = {
281
- type: "query",
282
- selects: [
283
- {
284
- type: "select",
285
- expr: { type: "field", tableAlias: "innerquery", column: schema.getTable(design.table).primaryKey },
286
- alias: "id"
287
- }, // main primary key as id
288
- { type: "select", expr: geometryExpr, alias: "the_geom_webmercator" }, // geometry as the_geom_webmercator
289
- cluster
290
- ],
291
- from: exprCompiler.compileTable(design.table, "innerquery")
292
- };
293
- // Add color select if color axis
294
- if (design.axes.color) {
295
- const colorExpr = axisBuilder.compileAxis({ axis: design.axes.color, tableAlias: "innerquery" });
296
- innerquery.selects.push({ type: "select", expr: colorExpr, alias: "color" });
297
- }
298
- // Create filters. First limit to bounding box
299
- let whereClauses = [
300
- {
301
- type: "op",
302
- op: "&&",
303
- exprs: [geometryExpr, { type: "token", token: "!bbox!" }]
304
- }
305
- ];
306
- // Then add filters baked into layer
307
- if (design.filter) {
308
- whereClauses.push(exprCompiler.compileExpr({ expr: design.filter, tableAlias: "innerquery" }));
309
- }
310
- // Then add extra filters passed in, if relevant
311
- // Get relevant filters
312
- const relevantFilters = lodash_1.default.where(filters, { table: design.table });
313
- for (let filter of relevantFilters) {
314
- whereClauses.push((0, expressions_1.injectTableAlias)(filter.jsonql, "innerquery"));
315
- }
316
- whereClauses = lodash_1.default.compact(whereClauses);
317
- // Wrap if multiple
318
- if (whereClauses.length > 1) {
319
- innerquery.where = { type: "op", op: "and", exprs: whereClauses };
320
- }
321
- else {
322
- innerquery.where = whereClauses[0];
323
- }
324
- // Create outer query which takes where r <= 3 to limit # of points in a cluster
325
- const outerquery = {
326
- type: "query",
327
- selects: [
328
- {
329
- type: "select",
330
- expr: { type: "field", tableAlias: "innerquery", column: "id" },
331
- alias: "id"
332
- }, // innerquery._id as id
333
- {
334
- type: "select",
335
- expr: { type: "field", tableAlias: "innerquery", column: "the_geom_webmercator" },
336
- alias: "the_geom_webmercator"
337
- }, // innerquery.the_geom_webmercator as the_geom_webmercator
338
- {
339
- type: "select",
340
- expr: {
341
- type: "op",
342
- op: "ST_GeometryType",
343
- exprs: [{ type: "field", tableAlias: "innerquery", column: "the_geom_webmercator" }]
344
- },
345
- alias: "geometry_type"
346
- } // ST_GeometryType(innerquery.the_geom_webmercator) as geometry_type
347
- ],
348
- from: { type: "subquery", query: innerquery, alias: "innerquery" },
349
- where: { type: "op", op: "<=", exprs: [{ type: "field", tableAlias: "innerquery", column: "r" }, 3] }
350
- };
351
- // Add color select if color axis
352
- if (design.axes.color) {
353
- outerquery.selects.push({
354
- type: "select",
355
- expr: { type: "field", tableAlias: "innerquery", column: "color" },
356
- alias: "color"
357
- }); // innerquery.color as color
358
- }
359
- return outerquery;
360
- }
361
- // Creates CartoCSS
362
- createCss(design) {
363
- let stroke, symbol;
364
- let css = "";
365
- if (design.symbol) {
366
- symbol = `marker-file: url(${design.symbol});`;
367
- stroke = "marker-line-width: 60;";
368
- }
369
- else {
370
- symbol = "marker-type: ellipse;";
371
- stroke = "marker-line-width: 1;";
372
- }
373
- // Should only display markers when it is a point geometry
374
- css +=
375
- `\
376
- #layer0[geometry_type='ST_Point'] {
377
- marker-fill: ` +
378
- (design.color || "#666666") +
379
- `;
380
- marker-width: ` +
381
- (design.markerSize || 10) +
382
- `;
383
- marker-line-color: white;\
384
- ` +
385
- stroke +
386
- `\
387
- marker-line-opacity: 0.6;
388
- marker-placement: point;\
389
- ` +
390
- symbol +
391
- `\
392
- marker-allow-overlap: true;
393
- }
394
- #layer0 {
395
- line-color: ` +
396
- (design.color || "#666666") +
397
- `;
398
- line-width: ` +
399
- (design.lineWidth != null ? design.lineWidth : "3") +
400
- `;
401
- }
402
- #layer0[geometry_type='ST_Polygon'],#layer0[geometry_type='ST_MultiPolygon'] {
403
- polygon-fill: ` +
404
- (design.color || "#666666") +
405
- `;
406
- polygon-opacity: ${design.polygonFillOpacity ?? 0.25};
407
- }
408
- \
409
- `;
410
- // If color axes, add color conditions
411
- if (design.axes.color && design.axes.color.colorMap) {
412
- for (let item of design.axes.color.colorMap) {
413
- // If invisible
414
- if (lodash_1.default.includes(design.axes.color.excludedValues || [], item.value)) {
415
- css +=
416
- `\
417
- #layer0[color=` +
418
- JSON.stringify(item.value) +
419
- `] { line-opacity: 0; marker-line-opacity: 0; marker-fill-opacity: 0; polygon-opacity: 0; }\
420
- `;
421
- }
422
- else {
423
- css +=
424
- `\
425
- #layer0[color=` +
426
- JSON.stringify(item.value) +
427
- "] { line-color: " +
428
- item.color +
429
- ` }
430
- #layer0[color=` +
431
- JSON.stringify(item.value) +
432
- "][geometry_type='ST_Point'] { marker-fill: " +
433
- item.color +
434
- ` }
435
- #layer0[color=` +
436
- JSON.stringify(item.value) +
437
- "][geometry_type='ST_Polygon'],#layer0[color=" +
438
- JSON.stringify(item.value) +
439
- `][geometry_type='ST_MultiPolygon'] {
440
- polygon-fill: ` +
441
- item.color +
442
- `;\
443
- ${design.polygonBorderColor ? "line-color: " + design.polygonBorderColor + ";" : ""}\
444
- }\
445
- `;
446
- }
447
- }
448
- }
449
- return css;
450
- }
451
255
  // same as onGridClick but handles hover over
452
256
  onGridHoverOver(ev, hoverOptions) {
453
257
  if (ev.data && ev.data.id && hoverOptions.design.hoverOver && hoverOptions.design.hoverOver.items.length > 0) {
@@ -591,6 +395,13 @@ ${design.polygonBorderColor ? "line-color: " + design.polygonBorderColor + ";" :
591
395
  }
592
396
  }
593
397
  const axisBuilder = new AxisBuilder_1.default({ schema });
398
+ // Clean and translate axis
399
+ const axis = (0, MapUtils_1.translateAxis)(axisBuilder.cleanAxis({
400
+ axis: design.axes.color || null,
401
+ table: design.table,
402
+ types: ["enum", "text", "boolean", "date"],
403
+ aggrNeed: "none"
404
+ }), translate);
594
405
  return react_1.default.createElement(LayerLegendComponent_1.default, {
595
406
  schema,
596
407
  defaultColor: design.color,
@@ -598,12 +409,7 @@ ${design.polygonBorderColor ? "line-color: " + design.polygonBorderColor + ";" :
598
409
  markerSize: design.markerSize,
599
410
  name: translate(name),
600
411
  filters: lodash_1.default.compact(_filters),
601
- axis: axisBuilder.cleanAxis({
602
- axis: design.axes.color || null,
603
- table: design.table,
604
- types: ["enum", "text", "boolean", "date"],
605
- aggrNeed: "none"
606
- }),
412
+ axis,
607
413
  locale
608
414
  });
609
415
  }
@@ -665,6 +471,12 @@ ${design.polygonBorderColor ? "line-color: " + design.polygonBorderColor + ";" :
665
471
  types: ["enum", "text", "boolean", "date"],
666
472
  aggrNeed: "none"
667
473
  });
474
+ draft.axes.label = axisBuilder.cleanAxis({
475
+ axis: draft.axes.label ? (0, immer_1.original)(draft.axes.label) || null : null,
476
+ table: design.table,
477
+ types: ["text", "number"],
478
+ aggrNeed: "none"
479
+ }) || undefined;
668
480
  draft.filter = exprCleaner.cleanExpr(design.filter || null, { table: draft.table });
669
481
  // Clean hover over expressions
670
482
  if (design.table && design.hoverOver && design.hoverOver.items) {
@@ -700,6 +512,11 @@ ${design.polygonBorderColor ? "line-color: " + design.polygonBorderColor + ";" :
700
512
  if (error) {
701
513
  return error;
702
514
  }
515
+ // Validate label
516
+ error = axisBuilder.validateAxis({ axis: design.axes.label || null });
517
+ if (error) {
518
+ return error;
519
+ }
703
520
  // Check that doesn't compile to null (persistent bug that haven't been able to track down)
704
521
  if (!axisBuilder.compileAxis({ axis: design.axes.geometry, tableAlias: "innerquery" })) {
705
522
  return "Null geometry axis";
@@ -714,6 +531,8 @@ ${design.polygonBorderColor ? "line-color: " + design.polygonBorderColor + ";" :
714
531
  /** Get strings to be translated */
715
532
  getTranslatableStrings(design, schema) {
716
533
  const strings = [];
534
+ // Add strings from axis category labels and null labels
535
+ strings.push(...(0, MapUtils_1.getTranslatableStringsFromAxis)(design.axes.color));
717
536
  // Add strings from hoverOver items
718
537
  if (design.hoverOver && design.hoverOver.items) {
719
538
  for (const item of design.hoverOver.items) {
@@ -13,7 +13,11 @@ export interface MarkersLayerDesign {
13
13
  geometry: Axis;
14
14
  /** Color axis (to split into series based on a color) */
15
15
  color?: Axis;
16
+ /** Label expression to display on/near markers (points only) */
17
+ label?: Axis;
16
18
  };
19
+ /** Position of label relative to marker. Default "bottom" */
20
+ labelPosition?: "top" | "bottom" | "left" | "right";
17
21
  /** Optional logical expression to filter by */
18
22
  filter?: Expr;
19
23
  /** Color of layer (e.g. #FF8800). Color axis overrides */
@@ -1,6 +1,8 @@
1
1
  import React from "react";
2
- import { DataSource, Schema } from "@mwater/expressions";
2
+ import { DataSource, Expr, Schema } from "@mwater/expressions";
3
3
  import { MarkersLayerDesign } from "./MarkersLayerDesign";
4
+ import { Axis } from "../axes/Axis";
5
+ import { JsonQLFilter } from "../JsonQLFilter";
4
6
  export interface MarkersLayerDesignerComponentProps {
5
7
  /** Schema to use */
6
8
  schema: Schema;
@@ -8,23 +10,23 @@ export interface MarkersLayerDesignerComponentProps {
8
10
  /** Design of the marker layer */
9
11
  design: MarkersLayerDesign;
10
12
  /** Called with new design */
11
- onDesignChange: any;
12
- filters?: any;
13
+ onDesignChange: (design: MarkersLayerDesign) => void;
14
+ filters?: JsonQLFilter[];
13
15
  }
16
+ /** Designer for a markers layer */
14
17
  export default class MarkersLayerDesignerComponent extends React.Component<MarkersLayerDesignerComponentProps> {
15
- update(updates: any): any;
16
- updateAxes(changes: any): any;
17
- handleTableChange: (table: any) => any;
18
- handleGeometryAxisChange: (axis: any) => any;
19
- handleColorAxisChange: (axis: any) => any;
20
- handleFilterChange: (expr: any) => any;
21
- handleColorChange: (color: any) => any;
22
- handlePolygonBorderColorChange: (polygonBorderColor: any) => any;
23
- handlePolygonFillOpacityChange: (polygonFillOpacity: any) => any;
24
- handleSymbolChange: (symbol: any) => any;
25
- handleNameChange: (e: any) => any;
26
- handleMarkerSizeChange: (markerSize: any) => any;
27
- handleLineWidthChange: (lineWidth: any) => any;
18
+ handleTableChange: (table: string) => void;
19
+ handleGeometryAxisChange: (axis: Axis) => void;
20
+ handleColorAxisChange: (axis: Axis | null) => void;
21
+ handleFilterChange: (expr: Expr) => void;
22
+ handleColorChange: (color: string) => void;
23
+ handlePolygonBorderColorChange: (polygonBorderColor: string) => void;
24
+ handlePolygonFillOpacityChange: (polygonFillOpacity: number) => void;
25
+ handleSymbolChange: (symbol: string) => void;
26
+ handleMarkerSizeChange: (markerSize: number) => void;
27
+ handleLineWidthChange: (lineWidth: number) => void;
28
+ handleLabelAxisChange: (axis: Axis | null) => void;
29
+ handleLabelPositionChange: (labelPosition: "top" | "bottom" | "left" | "right") => void;
28
30
  renderTable(): React.JSX.Element;
29
31
  renderGeometryAxis(): React.JSX.Element | undefined;
30
32
  renderColor(): React.JSX.Element | undefined;
@@ -34,6 +36,8 @@ export default class MarkersLayerDesignerComponent extends React.Component<Marke
34
36
  renderPolygonBorderColor(): React.JSX.Element | undefined;
35
37
  renderPolygonFillOpacity(): React.JSX.Element | undefined;
36
38
  renderFilter(): React.JSX.Element | null;
39
+ renderLabelAxis(): React.JSX.Element | undefined;
40
+ renderLabelPosition(): React.JSX.Element | undefined;
37
41
  renderPopup(): React.JSX.Element | null;
38
42
  renderHoverOver(): React.JSX.Element | null;
39
43
  render(): React.JSX.Element;
@@ -40,49 +40,68 @@ const PopupFilterJoinsUtils = __importStar(require("./PopupFilterJoinsUtils"));
40
40
  const ui = __importStar(require("@mwater/react-library/lib/bootstrap"));
41
41
  const EditHoverOver_1 = require("./EditHoverOver");
42
42
  const rc_slider_1 = __importDefault(require("rc-slider"));
43
- // Designer for a markers layer
43
+ const immer_1 = require("immer");
44
+ /** Designer for a markers layer */
44
45
  class MarkersLayerDesignerComponent extends react_1.default.Component {
45
- // Apply updates to design
46
- update(updates) {
47
- return this.props.onDesignChange(lodash_1.default.extend({}, this.props.design, updates));
48
- }
49
- // Update axes with specified changes
50
- updateAxes(changes) {
51
- const axes = lodash_1.default.extend({}, this.props.design.axes, changes);
52
- return this.update({ axes });
53
- }
54
46
  handleTableChange = (table) => {
55
- return this.update({ table });
47
+ this.props.onDesignChange((0, immer_1.produce)(this.props.design, draft => {
48
+ draft.table = table;
49
+ }));
56
50
  };
57
51
  handleGeometryAxisChange = (axis) => {
58
- return this.updateAxes({ geometry: axis });
52
+ this.props.onDesignChange((0, immer_1.produce)(this.props.design, draft => {
53
+ draft.axes.geometry = axis;
54
+ }));
59
55
  };
60
56
  handleColorAxisChange = (axis) => {
61
- return this.updateAxes({ color: axis });
57
+ this.props.onDesignChange((0, immer_1.produce)(this.props.design, draft => {
58
+ draft.axes.color = axis ?? undefined;
59
+ }));
62
60
  };
63
61
  handleFilterChange = (expr) => {
64
- return this.update({ filter: expr });
62
+ this.props.onDesignChange((0, immer_1.produce)(this.props.design, draft => {
63
+ draft.filter = expr;
64
+ }));
65
65
  };
66
66
  handleColorChange = (color) => {
67
- return this.update({ color });
67
+ this.props.onDesignChange((0, immer_1.produce)(this.props.design, draft => {
68
+ draft.color = color;
69
+ }));
68
70
  };
69
71
  handlePolygonBorderColorChange = (polygonBorderColor) => {
70
- return this.update({ polygonBorderColor });
72
+ this.props.onDesignChange((0, immer_1.produce)(this.props.design, draft => {
73
+ draft.polygonBorderColor = polygonBorderColor;
74
+ }));
71
75
  };
72
76
  handlePolygonFillOpacityChange = (polygonFillOpacity) => {
73
- return this.update({ polygonFillOpacity: polygonFillOpacity / 100 });
77
+ this.props.onDesignChange((0, immer_1.produce)(this.props.design, draft => {
78
+ draft.polygonFillOpacity = polygonFillOpacity / 100;
79
+ }));
74
80
  };
75
81
  handleSymbolChange = (symbol) => {
76
- return this.update({ symbol });
77
- };
78
- handleNameChange = (e) => {
79
- return this.update({ name: e.target.value });
82
+ this.props.onDesignChange((0, immer_1.produce)(this.props.design, draft => {
83
+ draft.symbol = symbol;
84
+ }));
80
85
  };
81
86
  handleMarkerSizeChange = (markerSize) => {
82
- return this.update({ markerSize });
87
+ this.props.onDesignChange((0, immer_1.produce)(this.props.design, draft => {
88
+ draft.markerSize = markerSize;
89
+ }));
83
90
  };
84
91
  handleLineWidthChange = (lineWidth) => {
85
- return this.update({ lineWidth });
92
+ this.props.onDesignChange((0, immer_1.produce)(this.props.design, draft => {
93
+ draft.lineWidth = lineWidth;
94
+ }));
95
+ };
96
+ handleLabelAxisChange = (axis) => {
97
+ this.props.onDesignChange((0, immer_1.produce)(this.props.design, draft => {
98
+ draft.axes.label = axis ?? undefined;
99
+ }));
100
+ };
101
+ handleLabelPositionChange = (labelPosition) => {
102
+ this.props.onDesignChange((0, immer_1.produce)(this.props.design, draft => {
103
+ draft.labelPosition = labelPosition;
104
+ }));
86
105
  };
87
106
  renderTable() {
88
107
  return (react_1.default.createElement("div", { className: "mb-3" },
@@ -207,6 +226,39 @@ class MarkersLayerDesignerComponent extends react_1.default.Component {
207
226
  react_1.default.createElement("div", { style: { marginLeft: 8 } },
208
227
  react_1.default.createElement(expressions_ui_1.FilterExprComponent, { schema: this.props.schema, dataSource: this.props.dataSource, onChange: this.handleFilterChange, table: this.props.design.table, value: this.props.design.filter }))));
209
228
  }
229
+ renderLabelAxis() {
230
+ if (!this.props.design.axes.geometry) {
231
+ return;
232
+ }
233
+ const filters = lodash_1.default.clone(this.props.filters) || [];
234
+ if (this.props.design.filter != null) {
235
+ const exprCompiler = new expressions_1.ExprCompiler(this.props.schema);
236
+ const jsonql = exprCompiler.compileExpr({ expr: this.props.design.filter, tableAlias: "{alias}" });
237
+ if (jsonql) {
238
+ filters.push({ table: this.props.design.filter.table, jsonql });
239
+ }
240
+ }
241
+ return (react_1.default.createElement("div", { className: "mb-3" },
242
+ react_1.default.createElement("label", { className: "text-muted" },
243
+ react_1.default.createElement("span", { className: "fas fa-tag" }),
244
+ " ",
245
+ T `Label (points only)`),
246
+ react_1.default.createElement(AxisComponent_1.default, { schema: this.props.schema, dataSource: this.props.dataSource, table: this.props.design.table, types: ["text", "number"], aggrNeed: "none", value: this.props.design.axes.label, onChange: this.handleLabelAxisChange, filters: filters })));
247
+ }
248
+ renderLabelPosition() {
249
+ // Only show if label axis is set
250
+ if (!this.props.design.axes.label) {
251
+ return;
252
+ }
253
+ return (react_1.default.createElement("div", { className: "mb-3" },
254
+ react_1.default.createElement("label", { className: "text-muted" }, T `Label Position`),
255
+ react_1.default.createElement(ui.Select, { value: this.props.design.labelPosition || "bottom", options: [
256
+ { value: "top", label: T `Top` },
257
+ { value: "bottom", label: T `Bottom` },
258
+ { value: "left", label: T `Left` },
259
+ { value: "right", label: T `Right` }
260
+ ], onChange: this.handleLabelPositionChange })));
261
+ }
210
262
  renderPopup() {
211
263
  if (!this.props.design.table) {
212
264
  return null;
@@ -226,6 +278,8 @@ class MarkersLayerDesignerComponent extends react_1.default.Component {
226
278
  this.renderColor(),
227
279
  this.renderSymbol(),
228
280
  this.renderMarkerSize(),
281
+ this.renderLabelAxis(),
282
+ this.renderLabelPosition(),
229
283
  react_1.default.createElement(ui.CollapsibleSection, { label: T `Shape Options`, labelMuted: true },
230
284
  this.renderLineWidth(),
231
285
  this.renderPolygonBorderColor(),
@@ -43,7 +43,6 @@ declare class ServerLayerDataSource implements MapLayerDataSource {
43
43
  options: ServerMapLayerDataSourceOptions;
44
44
  constructor(options: ServerMapLayerDataSourceOptions);
45
45
  getTileUrl(design: any, filters: JsonQLFilter[]): any;
46
- getUtfGridUrl(design: any, filters: JsonQLFilter[]): string | null;
47
46
  /** Get the url for vector tile source with an expiry time. Only for layers of type "VectorTile"
48
47
  * @param createdAfter ISO 8601 timestamp requiring that tile source on server is created after specified datetime
49
48
  */
@@ -76,21 +76,6 @@ class ServerLayerDataSource {
76
76
  }
77
77
  return this.createUrl(filters, "png");
78
78
  }
79
- // Get the url for the interactivity tiles with the specified filters applied
80
- // Called with (design, filters) where design is the design of the layer and filters are filters to apply. Returns URL
81
- getUtfGridUrl(design, filters) {
82
- // Handle special cases
83
- if (this.options.layerView.type === "MWaterServer") {
84
- return this.createLegacyUrl(design, "grid.json", filters);
85
- }
86
- // Create layer
87
- const layer = LayerFactory_1.default.createLayer(this.options.layerView.type);
88
- // If layer has tiles url directly available
89
- if (layer.getLayerDefinitionType() === "TileUrl") {
90
- return layer.getUtfGridUrl(this.options.layerView.design, filters);
91
- }
92
- return this.createUrl(filters, "grid.json");
93
- }
94
79
  /** Get the url for vector tile source with an expiry time. Only for layers of type "VectorTile"
95
80
  * @param createdAfter ISO 8601 timestamp requiring that tile source on server is created after specified datetime
96
81
  */
@@ -37,8 +37,6 @@ export default class SwitchableTileUrlLayer extends Layer<SwitchableTileUrlLayer
37
37
  getMaxZoom(design: SwitchableTileUrlLayerDesign): number;
38
38
  /** Gets the tile url for definition type "TileUrl" */
39
39
  getTileUrl(design: SwitchableTileUrlLayerDesign, filters: JsonQLFilter[]): string | null;
40
- /** Gets the utf grid url for definition type "TileUrl" */
41
- getUtfGridUrl(design: SwitchableTileUrlLayerDesign, filters: JsonQLFilter[]): string | null;
42
40
  getLegend(options: LegendOptions<SwitchableTileUrlLayerDesign>): ReactNode;
43
41
  /** True if layer can be edited */
44
42
  isEditable(): boolean;