Skip to content

Commit d7bade7

Browse files
committed
Use a prerendered image for the layer legend #658
1 parent fe23452 commit d7bade7

File tree

9 files changed

+101
-8
lines changed

9 files changed

+101
-8
lines changed

configure/src/metaconfigs/layer-image-config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@
190190
"new": true,
191191
"field": "legend",
192192
"name": "Legend From URL",
193-
"description": "A URL to a .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
193+
"description": "A URL to a static image or .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
194194
"type": "text",
195195
"width": 12
196196
}

configure/src/metaconfigs/layer-model-config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@
423423
"new": true,
424424
"field": "legend",
425425
"name": "Legend From URL",
426-
"description": "A URL to a .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
426+
"description": "A URL to a static image or .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
427427
"type": "text",
428428
"width": 12
429429
}

configure/src/metaconfigs/layer-query-config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@
546546
"new": true,
547547
"field": "legend",
548548
"name": "Legend From URL",
549-
"description": "A URL to a .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
549+
"description": "A URL to a static image or .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
550550
"type": "text",
551551
"width": 12
552552
}

configure/src/metaconfigs/layer-tile-config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@
396396
"new": true,
397397
"field": "legend",
398398
"name": "Legend From URL",
399-
"description": "A URL to a .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
399+
"description": "A URL to a static image or .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
400400
"type": "text",
401401
"width": 12
402402
}

configure/src/metaconfigs/layer-vector-config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@
484484
"new": true,
485485
"field": "legend",
486486
"name": "Legend From URL",
487-
"description": "A URL to a .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
487+
"description": "A URL to a static image or .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
488488
"type": "text",
489489
"width": 12
490490
}

configure/src/metaconfigs/layer-vectortile-config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@
440440
"new": true,
441441
"field": "legend",
442442
"name": "Legend From URL",
443-
"description": "A URL to a .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
443+
"description": "A URL to a static image or .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
444444
"type": "text",
445445
"width": 12
446446
}

configure/src/metaconfigs/layer-velocity-config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@
370370
"new": true,
371371
"field": "legend",
372372
"name": "Legend From URL",
373-
"description": "A URL to a .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
373+
"description": "A URL to a static image or .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
374374
"type": "text",
375375
"width": 12
376376
}

src/essence/Basics/Formulae_/Formulae_.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,11 @@ var Formulae_ = {
810810
},
811811
csvToJSON: function (csv) {
812812
if (csv == null) return {}
813+
814+
// Ensure csv is a string
815+
if (typeof csv !== 'string') {
816+
return {}
817+
}
813818

814819
var lines = csv.split('\n')
815820
var result = []

src/essence/Tools/Legend/LegendTool.js

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,56 @@ function refreshLegends() {
109109
layersTool.populateCogScale(L_.layers.data[l].name)
110110
}
111111

112+
// Check if there's a legend URL that points to an image
113+
const legendURL = L_.layers.data[l]?.legend
114+
if (legendURL && typeof legendURL === 'string') {
115+
let isImageUrl = false
116+
117+
// First check for file extensions
118+
const fileExtension = legendURL.toLowerCase().split('.').pop().split('?')[0] // Remove query params
119+
const imageExtensions = ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'tiff', 'tif', 'bmp', 'ico', 'avif']
120+
121+
if (imageExtensions.includes(fileExtension)) {
122+
isImageUrl = true
123+
} else if (['csv'].includes(fileExtension)) {
124+
isImageUrl = false
125+
} else {
126+
// If no file extension and not a csv, check for image MIME types in URL parameters (e.g., WMS GetLegendGraphic)
127+
try {
128+
const url = new URL(legendURL)
129+
const formatParam = url.searchParams.get('FORMAT') || url.searchParams.get('format')
130+
131+
if (formatParam) {
132+
const imageMimeTypes = [
133+
'image/png', 'image/jpeg', 'image/jpg', 'image/gif',
134+
'image/svg+xml', 'image/webp', 'image/tiff',
135+
'image/bmp', 'image/ico', 'image/avif'
136+
]
137+
138+
const decodedFormat = decodeURIComponent(formatParam).toLowerCase()
139+
if (imageMimeTypes.includes(decodedFormat)) {
140+
isImageUrl = true
141+
}
142+
}
143+
} catch (e) {
144+
// URL parsing failed, treat as non-image
145+
console.warn('Failed to parse legend URL:', legendURL)
146+
}
147+
}
148+
149+
if (isImageUrl) {
150+
// Handle image legend directly
151+
drawLegends(
152+
LegendTool.tools,
153+
legendURL, // Pass the URL string directly
154+
l,
155+
L_.layers.data[l].display_name,
156+
L_.layers.opacity[l]
157+
)
158+
continue; // Skip the CSV processing below
159+
}
160+
}
161+
112162
if (L_.layers.data[l]?._legend != undefined) {
113163
drawLegends(
114164
LegendTool.tools,
@@ -216,6 +266,44 @@ function drawLegends(tools, _legend, layerUUID, display_name, opacity) {
216266

217267
let lastContinues = []
218268
let lastShape = ''
269+
270+
// Check if _legend is an image URL (string)
271+
if (typeof _legend === 'string') {
272+
// Render image directly
273+
const imageContainer = c
274+
.append('div')
275+
.attr('class', 'legend-image-container')
276+
.style('display', 'flex')
277+
.style('justify-content', 'center')
278+
.style('margin', '8px')
279+
.style('padding', '8px')
280+
281+
imageContainer
282+
.append('img')
283+
.attr('src', _legend.startsWith('http') ? _legend : L_.missionPath + _legend)
284+
.attr('alt', `Legend for ${display_name}`)
285+
.style('max-width', '100%')
286+
.style('max-height', '220px')
287+
.style('height', 'auto')
288+
.style('background-color', 'white')
289+
.style('border', '1px solid var(--color-i)')
290+
.style('border-radius', '3px')
291+
.style('opacity', opacity)
292+
.on('error', function() {
293+
// Handle image load error
294+
d3.select(this.parentNode)
295+
.append('div')
296+
.style('color', '#ff6b6b')
297+
.style('padding', '8px')
298+
.style('text-align', 'center')
299+
.style('font-size', '12px')
300+
.text('Failed to load legend.')
301+
d3.select(this).remove()
302+
})
303+
304+
return // Exit early since we've rendered the image
305+
}
306+
219307
for (let d in _legend) {
220308
var shape = _legend[d].shapeImage && _legend[d].shapeImage.trim()
221309
? _legend[d].shapeImage : _legend[d].shapeIcon && _legend[d].shapeIcon.trim()
@@ -234,7 +322,7 @@ function drawLegends(tools, _legend, layerUUID, display_name, opacity) {
234322
})
235323
lastShape = shape
236324
} else {
237-
325+
238326
// finalize discreet and continuous
239327
if (lastContinues.length > 0) {
240328
pushScale(lastContinues)

0 commit comments

Comments
 (0)