Skip to content

Commit 76a5401

Browse files
#768 Branding and Dataset Attribution (#770)
* #768 Attributions 1 * #768 Attributions 2 * #768 Update toolconfigs
1 parent 7c3bde0 commit 76a5401

12 files changed

Lines changed: 323 additions & 15 deletions

File tree

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,26 @@
616616
"width": 12
617617
}
618618
]
619+
},
620+
{
621+
"subname": "Attribution",
622+
"subdescription": "Attribution information for the dataset. If configured, the attribution will be displayed horizontally below the scalebar when the layer is visible.",
623+
"components": [
624+
{
625+
"field": "attribution",
626+
"name": "Attribution Text",
627+
"description": "Attribution text to display for this layer's dataset (e.g., 'NASA/JPL-Caltech', 'USGS', 'ESA').",
628+
"type": "text",
629+
"width": 6
630+
},
631+
{
632+
"field": "attributionLink",
633+
"name": "Attribution Link",
634+
"description": "Optional URL to link the attribution text to (e.g., 'https://nasa.gov').",
635+
"type": "text",
636+
"width": 6
637+
}
638+
]
619639
}
620640
]
621641
},

configure/src/metaconfigs/tab-ui-config.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,34 @@
2020
}
2121
]
2222
},
23+
{
24+
"name": "Map Logo",
25+
"components": [
26+
{
27+
"field": "look.mapLogoUrl",
28+
"name": "Map Logo Image URL",
29+
"description": "URL path to a logo image to display on the map. Supports: (1) Absolute URLs (e.g., 'https://example.com/logo.png'), (2) Mission-relative paths (e.g., 'logo.png' for Missions/[mission]/logo.png), (3) Public assets (e.g., 'public/images/logos/logo.png'). The logo will appear in the bottom-right corner of the map and will be included in screenshots.",
30+
"type": "text",
31+
"width": 12
32+
},
33+
{
34+
"field": "look.mapLogoSize",
35+
"name": "Map Logo Size",
36+
"description": "Size of the map logo. Small: 64px, Medium: 128px, Large: 192px. Default is Medium.",
37+
"type": "dropdown",
38+
"width": 3,
39+
"options": ["small", "medium", "large"],
40+
"default": "medium"
41+
},
42+
{
43+
"field": "look.mapLogoLink",
44+
"name": "Map Logo Link URL",
45+
"description": "Optional URL to navigate to when the logo is clicked (e.g., 'https://mission.nasa.gov').",
46+
"type": "text",
47+
"width": 6
48+
}
49+
]
50+
},
2351
{
2452
"name": "Map Panel",
2553
"components": [

src/css/mmgis.css

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ body {
8585
--color-green2: #08ea58;
8686
--color-yellowgreen: #7eea08;
8787
--color-yellow: #d2b800;
88+
--color-gold: #a98732;
8889
--color-blue: #00b6d2;
8990
--color-orange: #d26100;
9091
--color-orange2: #c57907;
@@ -873,7 +874,10 @@ body {
873874
}
874875

875876
.leaflet-bottom.leaflet-left {
876-
transition: bottom 0.2s ease-in;
877+
transition: bottom 0.2s ease-out;
878+
}
879+
.leaflet-bottom.leaflet-right {
880+
transition: bottom 0.2s ease-out;
877881
}
878882

879883
.leaflet-popup-annotation > .leaflet-popup-content-wrapper {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/* Attributions styles */
2+
#mmgis-attributions {
3+
position: absolute;
4+
bottom: 0px;
5+
left: 0px;
6+
pointer-events: auto;
7+
z-index: 1000;
8+
font-size: 12px;
9+
color: var(--color-a5);
10+
background: var(--color-a);
11+
border-left: 1px solid black;
12+
padding: 2px 4px 2px 3px;
13+
line-height: 1.3;
14+
white-space: nowrap;
15+
overflow: hidden;
16+
letter-spacing: 0.5px;
17+
font-family: 'lato-light';
18+
text-overflow: ellipsis;
19+
}
20+
21+
#mmgis-attributions a {
22+
color: rgba(255, 255, 255, 0.9);
23+
text-decoration: none;
24+
}
25+
26+
#mmgis-attributions a:hover {
27+
color: rgba(255, 255, 255, 1);
28+
text-decoration: underline;
29+
}
30+
31+
#mmgis-attributions span {
32+
display: inline;
33+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Attributions displays dataset attributions below the scalebar
2+
import $ from 'jquery'
3+
import F_ from '../Basics/Formulae_/Formulae_'
4+
import L_ from '../Basics/Layers_/Layers_'
5+
6+
import './Attributions.css'
7+
8+
var Attributions = {
9+
visibleAttributions: [],
10+
init: function () {
11+
Attributions.update()
12+
},
13+
refresh: function () {
14+
Attributions.update()
15+
},
16+
remove: function () {
17+
$('#mmgis-attributions').remove()
18+
},
19+
update: function () {
20+
// Collect attributions from all visible layers
21+
const attributions = []
22+
const seen = new Set()
23+
24+
// Check all layers
25+
if (L_.layers && L_.layers.data) {
26+
Object.keys(L_.layers.data).forEach((layerName) => {
27+
const layer = L_.layers.data[layerName]
28+
29+
// Only include if layer is on and has attribution
30+
if (
31+
L_.layers.on[layerName] === true &&
32+
layer.attribution != null
33+
) {
34+
// Avoid duplicate attributions
35+
const key = `${layer.attribution}|${
36+
layer.attributionLink || ''
37+
}`
38+
console.log(key)
39+
if (!seen.has(key)) {
40+
seen.add(key)
41+
attributions.push({
42+
text: layer.attribution,
43+
link: layer.attributionLink || null,
44+
})
45+
}
46+
}
47+
})
48+
}
49+
50+
// Store current state
51+
Attributions.visibleAttributions = attributions
52+
53+
// Remove existing attribution display
54+
$('#mmgis-attributions').remove()
55+
56+
// Adjust mapToolBar height and compass position based on attribution presence
57+
const attributionHeight = 21
58+
if (attributions.length === 0) {
59+
// No attributions - restore original heights
60+
const currentHeight = parseInt($('#mapToolBar').css('height')) || 40
61+
if (currentHeight > 40) {
62+
$('#mapToolBar').css('height', '40px')
63+
}
64+
// Set compass to base position when no attributions
65+
$('#mmgis-map-compass').css('bottom', '38px')
66+
return
67+
}
68+
69+
// Build the attribution HTML
70+
const attributionItems = attributions.map((attr) => {
71+
if (attr.link && attr.link.length > 0) {
72+
return `<a href='${attr.link}' target='_blank' rel='noopener noreferrer'>${attr.text}</a>`
73+
} else {
74+
return `<span>${attr.text}</span>`
75+
}
76+
})
77+
78+
const attributionsHtml = [
79+
`<div id='mmgis-attributions'>`,
80+
`@ ${attributionItems.join(' | ')}`,
81+
`</div>`,
82+
].join('\n')
83+
84+
// Append to the leaflet bottom-left container (below scalebar)
85+
$('.leaflet-bottom.leaflet-left').append(attributionsHtml)
86+
87+
// Increase mapToolBar height to make room for attributions
88+
$('#mapToolBar').css('height', 40 + attributionHeight + 'px')
89+
90+
// Set compass position to account for attributions (38px base + 21px attribution height)
91+
$('#mmgis-map-compass').css('bottom', '59px')
92+
},
93+
}
94+
95+
export default Attributions

src/essence/Ancillary/Coordinates.js

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,9 @@ const Coordinates = {
124124
!(
125125
L_.configData.time &&
126126
L_.configData.time.enabled === true &&
127-
(
128-
L_.configData.time.visible === true ||
127+
(L_.configData.time.visible === true ||
129128
L_.configData.time.liveByDefault === true ||
130-
L_.FUTURES.live === true
131-
)
129+
L_.FUTURES.live === true)
132130
)
133131
) {
134132
$('#toggleTimeUI').css({ display: 'none' })
@@ -279,13 +277,10 @@ const Coordinates = {
279277
if (
280278
L_.configData.time &&
281279
L_.configData.time.enabled === true &&
282-
(
283-
L_.FUTURES.live === true ||
284-
(L_.FUTURES.live == null && (
285-
L_.configData.time.initiallyOpen === true ||
286-
L_.configData.time.liveByDefault === true
287-
))
288-
)
280+
(L_.FUTURES.live === true ||
281+
(L_.FUTURES.live == null &&
282+
(L_.configData.time.initiallyOpen === true ||
283+
L_.configData.time.liveByDefault === true)))
289284
) {
290285
toggleTimeUI()
291286
}
@@ -804,6 +799,12 @@ function toggleTimeUI() {
804799
$('.leaflet-bottom.leaflet-left').css({
805800
bottom: newBottom + 'px',
806801
})
802+
$('#mmgis-attributions').css({
803+
bottom: (UserInterface.pxIsTools || 0) + 'px',
804+
})
805+
$('.leaflet-bottom.leaflet-right').css({
806+
bottom: newBottom + (UserInterface.pxIsTools || 0) + 'px',
807+
})
807808
$('#photosphereAzIndicator').css({
808809
bottom: newBottom + (UserInterface.pxIsTools || 0) + 'px',
809810
transition: 'bottom 0.2s ease-in',

src/essence/Ancillary/MapLogo.css

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/* MapLogo styles */
2+
#mmgis-map-logo {
3+
position: absolute;
4+
bottom: 45px;
5+
right: 5px;
6+
pointer-events: auto;
7+
z-index: 1000;
8+
padding: 5px;
9+
}
10+
11+
#mmgis-map-logo a {
12+
display: block;
13+
line-height: 0;
14+
}
15+
16+
#mmgis-map-logo img {
17+
height: auto;
18+
}
19+
20+
#mmgis-map-logo a:hover {
21+
opacity: 0.8;
22+
}

src/essence/Ancillary/MapLogo.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// MapLogo displays a configurable logo on the map
2+
import $ from 'jquery'
3+
import F_ from '../Basics/Formulae_/Formulae_'
4+
import L_ from '../Basics/Layers_/Layers_'
5+
6+
import './MapLogo.css'
7+
8+
var MapLogo = {
9+
config: null,
10+
init: function (config) {
11+
MapLogo.config = config || {}
12+
13+
// Only initialize if mapLogoUrl is configured
14+
if (!MapLogo.config.mapLogoUrl) return
15+
16+
MapLogo.update()
17+
},
18+
refresh: function () {
19+
MapLogo.update()
20+
},
21+
remove: function () {
22+
$('#mmgis-map-logo').remove()
23+
},
24+
update: function () {
25+
// Only show if configured
26+
if (!MapLogo.config.mapLogoUrl) return
27+
28+
// Remove existing logo if present
29+
if ($('#mmgis-map-logo').length > 0) {
30+
$('#mmgis-map-logo').remove()
31+
}
32+
33+
// Process the logo URL - handle both absolute URLs and relative paths
34+
let logoUrl = MapLogo.config.mapLogoUrl
35+
36+
// If it's not an absolute URL, prepend the mission path
37+
// This handles relative paths like "Missions/MyMission/logo.png" or "public/images/logos/logo.png"
38+
if (!F_.isUrlAbsolute(logoUrl)) {
39+
// Check if it starts with 'public' (common for shared assets)
40+
// or if it needs the mission path prefix
41+
if (!logoUrl.startsWith('public/') && !logoUrl.startsWith('/')) {
42+
logoUrl = L_.missionPath + logoUrl
43+
}
44+
}
45+
46+
// Map size options to pixel widths
47+
const sizeMap = {
48+
small: 64,
49+
medium: 128,
50+
large: 192
51+
}
52+
const size = MapLogo.config.mapLogoSize || 'medium'
53+
const width = sizeMap[size] || sizeMap.medium
54+
55+
const hasLink = MapLogo.config.mapLogoLink && MapLogo.config.mapLogoLink.length > 0
56+
57+
// Build the logo HTML
58+
const logoHtml = [
59+
`<div id='mmgis-map-logo'>`,
60+
hasLink
61+
? `<a href='${MapLogo.config.mapLogoLink}' target='_blank' rel='noopener noreferrer'>`
62+
: '',
63+
`<img src='${logoUrl}' alt='Map Logo' style='width: ${width}px; height: auto; display: block;' />`,
64+
hasLink ? `</a>` : '',
65+
`</div>`
66+
].join('\n')
67+
68+
// Append to the leaflet bottom-right container
69+
$('.leaflet-bottom.leaflet-right').append(logoHtml)
70+
},
71+
}
72+
73+
export default MapLogo

src/essence/Basics/Layers_/Layers_.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import F_ from '../Formulae_/Formulae_'
33
import Description from '../../Ancillary/Description'
44
import Search from '../../Ancillary/Search'
5+
import Attributions from '../../Ancillary/Attributions'
56
import ToolController_ from '../../Basics/ToolController_/ToolController_'
67
import LayerGeologic from './LayerGeologic/LayerGeologic'
78
import $ from 'jquery'
@@ -299,6 +300,11 @@ const L_ = {
299300
// Always reupdate layer infos at the end to keep them in sync
300301
Description.updateInfo()
301302

303+
// Update attributions display
304+
if (typeof Attributions !== 'undefined' && Attributions.update) {
305+
Attributions.update()
306+
}
307+
302308
// Deselect active feature if its layer is being turned off
303309
if (L_.activeFeature && L_.activeFeature.layerName === s.name && on) {
304310
L_.setActiveFeature(null)

0 commit comments

Comments
 (0)