Merge pull request #1479 from umap-project/osm-template
Add a popup template to showcase OpenStreetMap data
This commit is contained in:
commit
fe61acd6c1
7 changed files with 202 additions and 59 deletions
|
@ -592,6 +592,8 @@ i.info {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
display: block;
|
display: block;
|
||||||
|
color: black;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
.umap-pictogram-choice img {
|
.umap-pictogram-choice img {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
|
@ -13,7 +13,7 @@ L.U.Browser = L.Class.extend({
|
||||||
zoom_to = L.DomUtil.create('i', 'feature-zoom_to', feature_li),
|
zoom_to = L.DomUtil.create('i', 'feature-zoom_to', feature_li),
|
||||||
edit = L.DomUtil.create('i', 'show-on-edit feature-edit', feature_li),
|
edit = L.DomUtil.create('i', 'show-on-edit feature-edit', feature_li),
|
||||||
del = L.DomUtil.create('i', 'show-on-edit feature-delete', feature_li),
|
del = L.DomUtil.create('i', 'show-on-edit feature-delete', feature_li),
|
||||||
color = L.DomUtil.create('i', 'feature-color', feature_li),
|
colorBox = L.DomUtil.create('i', 'feature-color', feature_li),
|
||||||
title = L.DomUtil.create('span', 'feature-title', feature_li),
|
title = L.DomUtil.create('span', 'feature-title', feature_li),
|
||||||
symbol = feature._getIconUrl
|
symbol = feature._getIconUrl
|
||||||
? L.U.Icon.prototype.formatUrl(feature._getIconUrl(), feature)
|
? L.U.Icon.prototype.formatUrl(feature._getIconUrl(), feature)
|
||||||
|
@ -22,8 +22,12 @@ L.U.Browser = L.Class.extend({
|
||||||
edit.title = L._('Edit this feature')
|
edit.title = L._('Edit this feature')
|
||||||
del.title = L._('Delete this feature')
|
del.title = L._('Delete this feature')
|
||||||
title.textContent = feature.getDisplayName() || '—'
|
title.textContent = feature.getDisplayName() || '—'
|
||||||
color.style.backgroundColor = feature.getOption('color')
|
const bgcolor = feature.getOption('color')
|
||||||
if (symbol) color.style.backgroundImage = `url(${symbol})`
|
colorBox.style.backgroundColor = bgcolor
|
||||||
|
if (symbol && symbol !== this.map.options.default_iconUrl) {
|
||||||
|
const icon = L.U.Icon.makeIconElement(symbol, colorBox)
|
||||||
|
L.U.Icon.setIconContrast(icon, colorBox, symbol, bgcolor)
|
||||||
|
}
|
||||||
L.DomEvent.on(
|
L.DomEvent.on(
|
||||||
zoom_to,
|
zoom_to,
|
||||||
'click',
|
'click',
|
||||||
|
|
|
@ -262,6 +262,18 @@ L.Util.hasVar = (value) => {
|
||||||
return typeof value === 'string' && value.indexOf('{') != -1
|
return typeof value === 'string' && value.indexOf('{') != -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
L.Util.isPath = function (value) {
|
||||||
|
return value && value.length && value.startsWith('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Util.isRemoteUrl = function (value) {
|
||||||
|
return value && value.length && value.startsWith('http')
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Util.isDataImage = function (value) {
|
||||||
|
return value && value.length && value.startsWith('data:image')
|
||||||
|
}
|
||||||
|
|
||||||
L.Util.copyToClipboard = function (textToCopy) {
|
L.Util.copyToClipboard = function (textToCopy) {
|
||||||
// https://stackoverflow.com/a/65996386
|
// https://stackoverflow.com/a/65996386
|
||||||
// Navigator clipboard api needs a secure context (https)
|
// Navigator clipboard api needs a secure context (https)
|
||||||
|
@ -391,8 +403,8 @@ L.DomUtil.after = (target, el) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
L.DomUtil.RGBRegex = /rgb *\( *([0-9]{1,3}) *, *([0-9]{1,3}) *, *([0-9]{1,3}) *\)/
|
L.DomUtil.RGBRegex = /rgb *\( *([0-9]{1,3}) *, *([0-9]{1,3}) *, *([0-9]{1,3}) *\)/
|
||||||
L.DomUtil.TextColorFromBackgroundColor = (el) => {
|
L.DomUtil.TextColorFromBackgroundColor = (el, bgcolor) => {
|
||||||
return L.DomUtil.contrastedColor(el) ? '#ffffff' : '#000000'
|
return L.DomUtil.contrastedColor(el, bgcolor) ? '#ffffff' : '#000000'
|
||||||
}
|
}
|
||||||
const _CACHE_CONSTRAST = {}
|
const _CACHE_CONSTRAST = {}
|
||||||
L.DomUtil.contrastedColor = (el, bgcolor) => {
|
L.DomUtil.contrastedColor = (el, bgcolor) => {
|
||||||
|
|
|
@ -372,6 +372,7 @@ L.FormBuilder.PopupContent = L.FormBuilder.Select.extend({
|
||||||
['Table', L._('Table')],
|
['Table', L._('Table')],
|
||||||
['GeoRSSImage', L._('GeoRSS (title + image)')],
|
['GeoRSSImage', L._('GeoRSS (title + image)')],
|
||||||
['GeoRSSLink', L._('GeoRSS (only link)')],
|
['GeoRSSLink', L._('GeoRSS (only link)')],
|
||||||
|
['OSM', L._('OpenStreetMap')],
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -538,8 +539,8 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
|
||||||
this.footer.innerHTML = ''
|
this.footer.innerHTML = ''
|
||||||
this.buildTabs()
|
this.buildTabs()
|
||||||
const value = this.value()
|
const value = this.value()
|
||||||
if (!value || value.startsWith('/')) this.showSymbolsTab()
|
if (!value || L.Util.isPath(value)) this.showSymbolsTab()
|
||||||
else if (value.startsWith('http')) this.showURLTab()
|
else if (L.Util.isRemoteUrl(value) || L.Util.isDataImage(value)) this.showURLTab()
|
||||||
else this.showCharsTab()
|
else this.showCharsTab()
|
||||||
const closeButton = L.DomUtil.createButton(
|
const closeButton = L.DomUtil.createButton(
|
||||||
'button action-button',
|
'button action-button',
|
||||||
|
@ -596,20 +597,6 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
|
||||||
this.body.innerHTML = ''
|
this.body.innerHTML = ''
|
||||||
},
|
},
|
||||||
|
|
||||||
isPath: function () {
|
|
||||||
const value = this.value()
|
|
||||||
return value && value.length && value.startsWith('/')
|
|
||||||
},
|
|
||||||
|
|
||||||
isRemoteUrl: function () {
|
|
||||||
const value = this.value()
|
|
||||||
return value && value.length && value.startsWith('http')
|
|
||||||
},
|
|
||||||
|
|
||||||
isImg: function () {
|
|
||||||
return this.isPath() || this.isRemoteUrl()
|
|
||||||
},
|
|
||||||
|
|
||||||
updatePreview: function () {
|
updatePreview: function () {
|
||||||
this.buttons.innerHTML = ''
|
this.buttons.innerHTML = ''
|
||||||
if (this.isDefault()) return
|
if (this.isDefault()) return
|
||||||
|
@ -617,13 +604,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
|
||||||
// Do not try to render URL with variables
|
// Do not try to render URL with variables
|
||||||
const box = L.DomUtil.create('div', 'umap-pictogram-choice', this.buttons)
|
const box = L.DomUtil.create('div', 'umap-pictogram-choice', this.buttons)
|
||||||
L.DomEvent.on(box, 'click', this.onDefine, this)
|
L.DomEvent.on(box, 'click', this.onDefine, this)
|
||||||
if (this.isImg()) {
|
const icon = L.U.Icon.makeIconElement(this.value(), box)
|
||||||
const img = L.DomUtil.create('img', '', box)
|
|
||||||
img.src = this.value()
|
|
||||||
} else {
|
|
||||||
const el = L.DomUtil.create('span', '', box)
|
|
||||||
el.textContent = this.value()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.button = L.DomUtil.createButton(
|
this.button = L.DomUtil.createButton(
|
||||||
'button action-button',
|
'button action-button',
|
||||||
|
@ -723,7 +704,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
|
||||||
|
|
||||||
showCharsTab: function () {
|
showCharsTab: function () {
|
||||||
this.openTab('chars')
|
this.openTab('chars')
|
||||||
const value = !this.isImg() ? this.value() : null
|
const value = !L.U.Icon.isImg(this.value()) ? this.value() : null
|
||||||
const input = this.buildInput(this.body, value)
|
const input = this.buildInput(this.body, value)
|
||||||
input.placeholder = L._('Type char or paste emoji')
|
input.placeholder = L._('Type char or paste emoji')
|
||||||
input.type = 'text'
|
input.type = 'text'
|
||||||
|
@ -731,7 +712,10 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
|
||||||
|
|
||||||
showURLTab: function () {
|
showURLTab: function () {
|
||||||
this.openTab('url')
|
this.openTab('url')
|
||||||
const value = this.isRemoteUrl() ? this.value() : null
|
const value =
|
||||||
|
L.Util.isRemoteUrl(this.value()) || L.Util.isDataImage(this.value())
|
||||||
|
? this.value()
|
||||||
|
: null
|
||||||
const input = this.buildInput(this.body, value)
|
const input = this.buildInput(this.body, value)
|
||||||
input.placeholder = L._('Add image URL')
|
input.placeholder = L._('Add image URL')
|
||||||
input.type = 'url'
|
input.type = 'url'
|
||||||
|
|
|
@ -67,15 +67,8 @@ L.U.Icon.Default = L.U.Icon.extend({
|
||||||
|
|
||||||
onAdd: function () {
|
onAdd: function () {
|
||||||
const src = this._getIconUrl('icon')
|
const src = this._getIconUrl('icon')
|
||||||
// Decide whether to switch svg to white or not, but do it
|
const bgcolor = this._getColor()
|
||||||
// only for internal SVG, as invert could do weird things
|
L.U.Icon.setIconContrast(this.elements.icon, this.elements.container, src, bgcolor)
|
||||||
if (src.startsWith('/') && src.endsWith('.svg')) {
|
|
||||||
const bgcolor = this._getColor()
|
|
||||||
// Must be called after icon container is added to the DOM
|
|
||||||
if (L.DomUtil.contrastedColor(this.elements.container, bgcolor)) {
|
|
||||||
this.elements.img.style.filter = 'invert(1)'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
createIcon: function () {
|
createIcon: function () {
|
||||||
|
@ -89,18 +82,7 @@ L.U.Icon.Default = L.U.Icon.extend({
|
||||||
this.elements.arrow = L.DomUtil.create('div', 'icon_arrow', this.elements.main)
|
this.elements.arrow = L.DomUtil.create('div', 'icon_arrow', this.elements.main)
|
||||||
const src = this._getIconUrl('icon')
|
const src = this._getIconUrl('icon')
|
||||||
if (src) {
|
if (src) {
|
||||||
// An url.
|
this.elements.icon = L.U.Icon.makeIconElement(src, this.elements.container)
|
||||||
if (
|
|
||||||
src.startsWith('http') ||
|
|
||||||
src.startsWith('/') ||
|
|
||||||
src.startsWith('data:image')
|
|
||||||
) {
|
|
||||||
this.elements.img = L.DomUtil.create('img', null, this.elements.container)
|
|
||||||
this.elements.img.src = src
|
|
||||||
} else {
|
|
||||||
this.elements.span = L.DomUtil.create('span', null, this.elements.container)
|
|
||||||
this.elements.span.textContent = src
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this._setIconStyles(this.elements.main, 'icon')
|
this._setIconStyles(this.elements.main, 'icon')
|
||||||
return this.elements.main
|
return this.elements.main
|
||||||
|
@ -208,3 +190,44 @@ L.U.Icon.Cluster = L.DivIcon.extend({
|
||||||
return color || L.DomUtil.TextColorFromBackgroundColor(el, backgroundColor)
|
return color || L.DomUtil.TextColorFromBackgroundColor(el, backgroundColor)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
L.U.Icon.isImg = function (src) {
|
||||||
|
return L.Util.isPath(src) || L.Util.isRemoteUrl(src) || L.Util.isDataImage(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
L.U.Icon.makeIconElement = function (src, parent) {
|
||||||
|
let icon
|
||||||
|
if (L.U.Icon.isImg(src)) {
|
||||||
|
icon = L.DomUtil.create('img')
|
||||||
|
icon.src = src
|
||||||
|
} else {
|
||||||
|
icon = L.DomUtil.create('span')
|
||||||
|
icon.textContent = src
|
||||||
|
}
|
||||||
|
parent.appendChild(icon)
|
||||||
|
return icon
|
||||||
|
}
|
||||||
|
|
||||||
|
L.U.Icon.setIconContrast = function (el, parent, src, bgcolor) {
|
||||||
|
/*
|
||||||
|
* el: the element we'll adapt the style, it can be an image or text
|
||||||
|
* parent: the element we'll consider to decide whether to adapt the style,
|
||||||
|
* by looking at its background color
|
||||||
|
* src: the raw "icon" value, can be an URL, a path, text, emoticon, etc.
|
||||||
|
* bgcolor: the background color, used for caching and in case we cannot guess the
|
||||||
|
* parent background color
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (L.DomUtil.contrastedColor(parent, bgcolor)) {
|
||||||
|
// Decide whether to switch svg to white or not, but do it
|
||||||
|
// only for internal SVG, as invert could do weird things
|
||||||
|
if (L.Util.isPath(src) && src.endsWith('.svg')) {
|
||||||
|
// Must be called after icon container is added to the DOM
|
||||||
|
// An image
|
||||||
|
el.style.filter = 'invert(1)'
|
||||||
|
} else if (!el.src) {
|
||||||
|
// Text icon
|
||||||
|
el.style.color = 'white'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -251,3 +251,90 @@ L.U.PopupTemplate.GeoRSSLink = L.U.PopupTemplate.Default.extend({
|
||||||
return a
|
return a
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
L.U.PopupTemplate.OSM = L.U.PopupTemplate.Default.extend({
|
||||||
|
options: {
|
||||||
|
className: 'umap-openstreetmap',
|
||||||
|
},
|
||||||
|
|
||||||
|
getName: function () {
|
||||||
|
const props = this.feature.properties
|
||||||
|
if (L.locale && props[`name:${L.locale}`]) return props[`name:${L.locale}`]
|
||||||
|
return props.name
|
||||||
|
},
|
||||||
|
|
||||||
|
renderBody: function () {
|
||||||
|
const props = this.feature.properties
|
||||||
|
const container = L.DomUtil.add('div')
|
||||||
|
const title = L.DomUtil.add('h3', 'popup-title', container)
|
||||||
|
const color = this.feature.getDynamicOption('color')
|
||||||
|
title.style.backgroundColor = color
|
||||||
|
const iconUrl = this.feature.getDynamicOption('iconUrl')
|
||||||
|
let icon = L.U.Icon.makeIconElement(iconUrl, title)
|
||||||
|
L.DomUtil.addClass(icon, 'icon')
|
||||||
|
L.U.Icon.setIconContrast(icon, title, iconUrl, color)
|
||||||
|
if (L.DomUtil.contrastedColor(title, color)) title.style.color = 'white'
|
||||||
|
L.DomUtil.add('span', '', title, this.getName())
|
||||||
|
const street = props['addr:street']
|
||||||
|
if (street) {
|
||||||
|
const row = L.DomUtil.add('address', 'address', container)
|
||||||
|
const number = props['addr:housenumber']
|
||||||
|
if (number) {
|
||||||
|
// Poor way to deal with international forms of writting addresses
|
||||||
|
L.DomUtil.add('span', '', row, `${L._('No.')}: ${number}`)
|
||||||
|
L.DomUtil.add('span', '', row, `${L._('Street')}: ${street}`)
|
||||||
|
} else {
|
||||||
|
L.DomUtil.add('span', '', row, street)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (props.website) {
|
||||||
|
L.DomUtil.element(
|
||||||
|
'a',
|
||||||
|
{ href: props.website, textContent: props.website },
|
||||||
|
container
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const phone = props.phone || props['contact:phone']
|
||||||
|
if (phone) {
|
||||||
|
L.DomUtil.add(
|
||||||
|
'div',
|
||||||
|
'',
|
||||||
|
container,
|
||||||
|
L.DomUtil.element('a', { href: `tel:${phone}`, textContent: phone })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (props.mobile) {
|
||||||
|
L.DomUtil.add(
|
||||||
|
'div',
|
||||||
|
'',
|
||||||
|
container,
|
||||||
|
L.DomUtil.element('a', {
|
||||||
|
href: `tel:${props.mobile}`,
|
||||||
|
textContent: props.mobile,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const email = props.email || props['contact:email']
|
||||||
|
if (email) {
|
||||||
|
L.DomUtil.add(
|
||||||
|
'div',
|
||||||
|
'',
|
||||||
|
container,
|
||||||
|
L.DomUtil.element('a', { href: `mailto:${email}`, textContent: email })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const id = props['@id']
|
||||||
|
if (id) {
|
||||||
|
L.DomUtil.add(
|
||||||
|
'div',
|
||||||
|
'osm-link',
|
||||||
|
container,
|
||||||
|
L.DomUtil.element('a', {
|
||||||
|
href: `https://www.openstreetmap.org/${id}`,
|
||||||
|
textContent: L._('See on OpenStreetMap'),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return container
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
|
@ -1043,29 +1043,36 @@ a.add-datalayer:hover,
|
||||||
background-color: #f8f8f3;
|
background-color: #f8f8f3;
|
||||||
}
|
}
|
||||||
.umap-browse-features .feature-color {
|
.umap-browse-features .feature-color {
|
||||||
box-shadow: 0 0 4px 0 black inset;
|
box-shadow: 0 0 2px 0 black inset;
|
||||||
background-size: 70% 70%;
|
|
||||||
border: 4px solid #f8f8f3;
|
|
||||||
cursor: inherit;
|
cursor: inherit;
|
||||||
-moz-box-sizing:border-box;
|
-moz-box-sizing:border-box;
|
||||||
-webkit-box-sizing:border-box;
|
-webkit-box-sizing:border-box;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-position: center;
|
background: none;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
|
text-align: center;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
.umap-browse-features .feature-color img {
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
.umap-browse-features .feature-color span {
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
.umap-browse-features .polygon .feature-color,
|
.umap-browse-features .polygon .feature-color,
|
||||||
.umap-browse-features .polyline .feature-color {
|
.umap-browse-features .polyline .feature-color {
|
||||||
box-shadow: 0 0 4px 0 black inset;
|
box-shadow: 0 0 2px 0 black inset;
|
||||||
background-image: url('./img/24.svg');
|
background-image: url('./img/24.svg');
|
||||||
background-size: 500%;
|
background-size: 500%;
|
||||||
}
|
}
|
||||||
.umap-browse-features .polyline .feature-color {
|
.umap-browse-features .polyline .feature-color {
|
||||||
background-position: -48px -16px;
|
background-position: -72px -23px;
|
||||||
}
|
}
|
||||||
.umap-browse-features .polygon .feature-color {
|
.umap-browse-features .polygon .feature-color {
|
||||||
background-position: -32px -16px;
|
background-position: -48px -25px;
|
||||||
}
|
}
|
||||||
.show-on-edit {
|
.show-on-edit {
|
||||||
display: none!important;
|
display: none!important;
|
||||||
|
@ -1299,6 +1306,31 @@ a.add-datalayer:hover,
|
||||||
.umap-popup a:hover {
|
.umap-popup a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
.popup-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.popup-title .icon {
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
a[href^='mailto']::before {
|
||||||
|
content: '🖃 ';
|
||||||
|
}
|
||||||
|
a[href^='tel']::before {
|
||||||
|
content: '🕿 ';
|
||||||
|
}
|
||||||
|
address span {
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
.osm-link {
|
||||||
|
margin-top: 10px;
|
||||||
|
text-align: right;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
span.popup-icon {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ************* */
|
/* ************* */
|
||||||
/* Marker's Icon */
|
/* Marker's Icon */
|
||||||
|
@ -1387,7 +1419,6 @@ a.add-datalayer:hover,
|
||||||
.umap-div-icon .icon_container span,
|
.umap-div-icon .icon_container span,
|
||||||
.umap-drop-icon .icon_container span {
|
.umap-drop-icon .icon_container span {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
color: white;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
.umap-circle-icon {
|
.umap-circle-icon {
|
||||||
|
|
Loading…
Reference in a new issue