@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.
- package/lib/ColorComponent.js +2 -2
- package/lib/TranslationsTabComponent.d.ts +34 -0
- package/lib/TranslationsTabComponent.js +256 -0
- package/lib/dashboards/DashboardComponent.js +1 -1
- package/lib/dashboards/ServerDashboardDataSource.d.ts +0 -1
- package/lib/dashboards/ServerDashboardDataSource.js +0 -15
- package/lib/dashboards/SettingsModalComponent.js +9 -233
- package/lib/datagrids/DatagridComponent.js +5 -0
- package/lib/datagrids/DatagridViewComponent.js +30 -4
- package/lib/maps/BufferLayer.d.ts +0 -13
- package/lib/maps/BufferLayer.js +12 -237
- package/lib/maps/BufferLayerDesignerComponent.d.ts +1 -1
- package/lib/maps/BufferLayerDesignerComponent.js +0 -5
- package/lib/maps/ChoroplethLayer.d.ts +1 -16
- package/lib/maps/ChoroplethLayer.js +13 -358
- package/lib/maps/ClusterLayer.d.ts +0 -9
- package/lib/maps/ClusterLayer.js +0 -250
- package/lib/maps/DirectMapDataSource.js +1 -38
- package/lib/maps/GridLayer.d.ts +0 -15
- package/lib/maps/GridLayer.js +0 -212
- package/lib/maps/Layer.d.ts +1 -26
- package/lib/maps/Layer.js +0 -13
- package/lib/maps/MapComponent.d.ts +19 -35
- package/lib/maps/MapComponent.js +135 -76
- package/lib/maps/MapControlComponent.d.ts +4 -5
- package/lib/maps/MapControlComponent.js +5 -12
- package/lib/maps/MapDesign.d.ts +8 -0
- package/lib/maps/MapDesignerComponent.d.ts +2 -0
- package/lib/maps/MapDesignerComponent.js +7 -2
- package/lib/maps/MapLayerDataSource.d.ts +0 -4
- package/lib/maps/MapLayerViewDesignerComponent.d.ts +3 -1
- package/lib/maps/MapLayerViewDesignerComponent.js +5 -1
- package/lib/maps/MapLayersDesignerComponent.d.ts +2 -0
- package/lib/maps/MapLayersDesignerComponent.js +2 -1
- package/lib/maps/MapTranslationsTab.d.ts +15 -0
- package/lib/maps/MapTranslationsTab.js +47 -0
- package/lib/maps/MapUtils.d.ts +11 -0
- package/lib/maps/MapUtils.js +47 -0
- package/lib/maps/MapViewComponent.d.ts +1 -1
- package/lib/maps/MapViewComponent.js +1 -8
- package/lib/maps/MarkersLayer.d.ts +1 -14
- package/lib/maps/MarkersLayer.js +71 -252
- package/lib/maps/MarkersLayerDesign.d.ts +4 -0
- package/lib/maps/MarkersLayerDesignerComponent.d.ts +20 -16
- package/lib/maps/MarkersLayerDesignerComponent.js +77 -23
- package/lib/maps/ServerMapDataSource.d.ts +0 -1
- package/lib/maps/ServerMapDataSource.js +0 -15
- package/lib/maps/SwitchableTileUrlLayer.d.ts +0 -2
- package/lib/maps/SwitchableTileUrlLayer.js +0 -9
- package/lib/maps/TileUrlLayer.d.ts +0 -1
- package/lib/maps/TileUrlLayer.js +0 -5
- package/lib/maps/VectorMapViewComponent.js +12 -1
- package/lib/maps/vectorMaps.d.ts +5 -6
- package/lib/maps/vectorMaps.js +13 -9
- package/lib/widgets/MapWidget.js +2 -1
- package/package.json +2 -2
- package/src/ColorComponent.tsx +2 -2
- package/src/TranslationsTabComponent.tsx +429 -0
- package/src/dashboards/DashboardComponent.tsx +1 -1
- package/src/dashboards/ServerDashboardDataSource.ts +0 -19
- package/src/dashboards/SettingsModalComponent.tsx +27 -383
- package/src/datagrids/DatagridComponent.tsx +6 -0
- package/src/datagrids/DatagridViewComponent.tsx +41 -5
- package/src/maps/BufferLayer.ts +16 -262
- package/src/maps/BufferLayerDesignerComponent.tsx +0 -6
- package/src/maps/ChoroplethLayer.ts +16 -393
- package/src/maps/ClusterLayer.ts +0 -274
- package/src/maps/DirectMapDataSource.ts +2 -49
- package/src/maps/GridLayer.ts +0 -224
- package/src/maps/Layer.ts +1 -35
- package/src/maps/MapComponent.tsx +448 -0
- package/src/maps/MapControlComponent.tsx +41 -0
- package/src/maps/MapDesign.ts +6 -0
- package/src/maps/MapDesignerComponent.tsx +18 -1
- package/src/maps/MapLayerDataSource.ts +0 -5
- package/src/maps/MapLayerViewDesignerComponent.ts +9 -2
- package/src/maps/MapLayersDesignerComponent.ts +4 -1
- package/src/maps/MapTranslationsTab.tsx +53 -0
- package/src/maps/MapUtils.ts +48 -0
- package/src/maps/MapViewComponent.tsx +2 -8
- package/src/maps/MarkersLayer.ts +79 -270
- package/src/maps/MarkersLayerDesign.ts +6 -0
- package/src/maps/MarkersLayerDesignerComponent.tsx +114 -38
- package/src/maps/ServerMapDataSource.ts +0 -19
- package/src/maps/SwitchableTileUrlLayer.tsx +0 -11
- package/src/maps/TileUrlLayer.tsx +0 -6
- package/src/maps/VectorMapViewComponent.tsx +13 -2
- package/src/maps/vectorMaps.tsx +12 -9
- package/src/widgets/MapWidget.tsx +2 -0
- package/src/maps/MapComponent.ts +0 -311
- package/src/maps/MapControlComponent.ts +0 -46
- 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 {
|
|
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;
|
package/lib/maps/MarkersLayer.js
CHANGED
|
@@ -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
|
|
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:
|
|
12
|
-
filters?:
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
|
|
47
|
+
this.props.onDesignChange((0, immer_1.produce)(this.props.design, draft => {
|
|
48
|
+
draft.table = table;
|
|
49
|
+
}));
|
|
56
50
|
};
|
|
57
51
|
handleGeometryAxisChange = (axis) => {
|
|
58
|
-
|
|
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
|
-
|
|
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
|
-
|
|
62
|
+
this.props.onDesignChange((0, immer_1.produce)(this.props.design, draft => {
|
|
63
|
+
draft.filter = expr;
|
|
64
|
+
}));
|
|
65
65
|
};
|
|
66
66
|
handleColorChange = (color) => {
|
|
67
|
-
|
|
67
|
+
this.props.onDesignChange((0, immer_1.produce)(this.props.design, draft => {
|
|
68
|
+
draft.color = color;
|
|
69
|
+
}));
|
|
68
70
|
};
|
|
69
71
|
handlePolygonBorderColorChange = (polygonBorderColor) => {
|
|
70
|
-
|
|
72
|
+
this.props.onDesignChange((0, immer_1.produce)(this.props.design, draft => {
|
|
73
|
+
draft.polygonBorderColor = polygonBorderColor;
|
|
74
|
+
}));
|
|
71
75
|
};
|
|
72
76
|
handlePolygonFillOpacityChange = (polygonFillOpacity) => {
|
|
73
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
87
|
+
this.props.onDesignChange((0, immer_1.produce)(this.props.design, draft => {
|
|
88
|
+
draft.markerSize = markerSize;
|
|
89
|
+
}));
|
|
83
90
|
};
|
|
84
91
|
handleLineWidthChange = (lineWidth) => {
|
|
85
|
-
|
|
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;
|