Compare commits

..

3 commits

Author SHA1 Message Date
Yohan Boniface
ba3a1ccd33 feat: display importers in a dialog instead of direclty in the form
The goal is to keep the form smaller, specifically to keep the
submit button visible as much as possible.
2024-12-02 19:50:46 +01:00
Yohan Boniface
88ee836b34 chore: make that Importer extends WithTemplate
Another step is needed to use this.elements instead of this.qs
2024-12-02 19:23:47 +01:00
Yohan Boniface
4d1286e0ef feat: add icon-magic in 16-white.svg 2024-12-02 19:04:30 +01:00
29 changed files with 85 additions and 312 deletions

View file

@ -47,7 +47,7 @@ Voici un bref passage en revu des différents imports proposés et pour finir l
## 1. Importer le contour dune commune
Cliquez sur loutil dimportation en bas de la barre de droite, puis cliquez sur le lien « Assistants dimport ».
Cliquez sur loutil dimportation en bas de la barre de droite, puis descendez jusquau cadre « Assistants dimport ».
Cliquez sur « Communes France » et sélectionnez la commune souhaitée dans une liste déroulante. Une fois la commune sélectionnée, le format est reconnu automatiquement (geojson) puis le type de calque (cliquer sur « ? » pour savoir quel choix opérer)
@ -64,7 +64,7 @@ Une fois cet import réalisé, tout est réglable : couleur de contour, de fond,
## 2. Importer les contours des départements ou des régions
Cliquez sur loutil dimportation en bas de la barre de droite, puis cliquez sur le lien « Assistants dimport ».
Cliquez sur loutil dimportation en bas de la barre de droite, puis descendez jusquau cadre « Assistants dimport ».
Cliquez sur « Contours nationaux » puis soit départements, soit régions et enfin le type de calque (voir supra lexplication). Tous les départements sont importés :
@ -72,7 +72,7 @@ Cliquez sur « Contours nationaux » puis soit départements, soit régions et
## 3. Importer un point dintérêt issu de GeoDataMine
Cliquez sur loutil dimportation en bas de la barre de droite, puis cliquez sur le lien « Assistants dimport ».
Cliquez sur loutil dimportation en bas de la barre de droite, puis descendez jusquau cadre « Assistants dimport ».
Cliquez sur « GeoDataMine (thèmes OSM) » et sélectionnez les informations souhaitées, routes, bâtiments, commerces, services publics, …
Par exemple, en sélectionnant les points deau potable de la CA du Grand Avignon, puis « Copier dans un calque »

View file

@ -1,5 +1,5 @@
# Force rtfd to use a recent version of mkdocs
mkdocs==1.6.1
pymdown-extensions==10.12
mkdocs-material==9.5.47
mkdocs-material==9.5.44
mkdocs-static-i18n==1.2.3

Binary file not shown.

Before

Width:  |  Height:  |  Size: 734 KiB

After

Width:  |  Height:  |  Size: 3.4 MiB

View file

@ -175,6 +175,11 @@ UMAP_EXTRA_URLS = {
}
```
#### UMAP_KEEP_VERSIONS
How many datalayer versions to keep. 10 by default.
#### UMAP_DEFAULT_EDIT_STATUS
Define the map default edit status.
@ -265,16 +270,6 @@ Available importers:
- `communesfr`: download French communes boundaries, from https://geo.api.gouv.fr/
- `datasets`: define URLs you want to promote to users, with a `name` and a `format`
#### UMAP_KEEP_VERSIONS
How many datalayer versions to keep. 10 by default.
#### UMAP_LABEL_KEYS
List of properties to consider as "Feature label" (to show in popup or in browser).
UMAP_LABEL_KEYS = ["name", "title"]
#### UMAP_MAPS_PER_PAGE
How many maps to show in maps list, like search or home page.

View file

@ -1,5 +1,5 @@
# Force rtfd to use a recent version of mkdocs
mkdocs==1.6.1
pymdown-extensions==10.12
mkdocs-material==9.5.47
mkdocs-material==9.5.44
mkdocs-static-i18n==1.2.3

View file

@ -44,10 +44,10 @@ dependencies = [
[project.optional-dependencies]
dev = [
"hatch==1.13.0",
"ruff==0.8.1",
"djlint==1.36.3",
"ruff==0.7.4",
"djlint==1.36.1",
"mkdocs==1.6.1",
"mkdocs-material==9.5.47",
"mkdocs-material==9.5.44",
"mkdocs-static-i18n==1.2.3",
"vermin==1.6.0",
"pymdown-extensions==10.12",
@ -56,10 +56,10 @@ dev = [
test = [
"factory-boy==3.3.1",
"playwright>=1.39",
"pytest==8.3.4",
"pytest==8.3.3",
"pytest-django==4.9.0",
"pytest-playwright==0.6.2",
"pytest-rerunfailures==15.0",
"pytest-playwright==0.5.2",
"pytest-rerunfailures==14.0",
"pytest-xdist>=3.5.0,<4",
]
docker = [
@ -68,7 +68,7 @@ docker = [
sync = [
"channels==4.2.0",
"daphne==4.1.2",
"pydantic==2.10.2",
"pydantic==2.9.2",
"websockets==13.1",
]

View file

@ -273,7 +273,6 @@ UMAP_HOME_FEED = "latest"
UMAP_IMPORTERS = {}
UMAP_HOST_INFOS = {}
UMAP_PURGATORY_ROOT = "/tmp/umappurgatory"
UMAP_LABEL_KEYS = ["name", "title"]
UMAP_READONLY = env("UMAP_READONLY", default=False)
UMAP_GZIP = True

View file

@ -65,9 +65,7 @@ body.login header {
.login-grid .login-openstreetmap-oauth2 {
background-image: url("./openstreetmap.png");
}
.login-grid .login-keycloak {
background-image: url("./keycloak.png");
}
/* **************************** */
/* home */

View file

@ -78,7 +78,8 @@ input[type="submit"] {
border-radius: 2px;
font-weight: normal;
cursor: pointer;
padding: 7px 14px;
padding: 7px;
width: 100%;
min-height: 32px;
line-height: 32px;
border: none;
@ -91,12 +92,6 @@ input[type="submit"] {
color: var(--text-color);
border: 1px solid #1b1f20;
}
.dark .button.primary:not([disabled]),
.dark [type="button"].primary:not([disabled]) {
background-color: var(--color-brightCyan);
color: var(--color-dark);
border: 1px solid #1b1f20;
}
.dark .button:hover,
.dark [type="button"]:hover,
.dark input[type="submit"]:hover {
@ -105,11 +100,6 @@ input[type="submit"] {
.dark a {
color: var(--text-color);
}
.dark [type="button"][disabled],
.dark input[type="submit"][disabled] {
background-color: var(--color-mediumGray);
cursor: not-allowed;
}
button.flat,
[type="button"].flat,
.dark [type="button"].flat {

View file

@ -165,9 +165,7 @@ class Feature {
}
getSlug() {
return (
this.properties[this._umap.getProperty('slugKey') || U.DEFAULT_LABEL_KEY] || ''
)
return this.properties[this._umap.getProperty('slugKey') || 'name'] || ''
}
getPermalink() {
@ -236,23 +234,15 @@ class Feature {
container.appendChild(builder.build())
const properties = []
let labelKeyFound = undefined
for (const property of this.datalayer._propertiesIndex) {
if (!labelKeyFound && U.LABEL_KEYS.includes(property)) {
labelKeyFound = property
continue
}
if (property === 'description') {
if (['name', 'description'].includes(property)) {
continue
}
properties.push([`properties.${property}`, { label: property }])
}
// We always want name and description for now (properties management to come)
properties.unshift('properties.description')
if (!labelKeyFound) {
labelKeyFound = U.DEFAULT_LABEL_KEY
}
properties.unshift([`properties.${labelKeyFound}`, { label: labelKeyFound }])
properties.unshift('properties.name')
builder = new U.FormBuilder(this, properties, {
id: 'umap-feature-properties',
})
@ -265,7 +255,7 @@ class Feature {
this.getAdvancedEditActions(advancedActions)
const onLoad = this._umap.editPanel.open({ content: container })
onLoad.then(() => {
builder.helpers[`properties.${labelKeyFound}`].input.focus()
builder.helpers['properties.name'].input.focus()
})
this._umap.editedFeature = this
if (!this.ui.isOnScreen(this._umap._leafletMap.getBounds())) this.zoomTo(event)
@ -326,21 +316,19 @@ class Feature {
endEdit() {}
getDisplayName() {
const keys = U.LABEL_KEYS.slice() // Copy.
const labelKey = this.getOption('labelKey')
getDisplayName(fallback) {
const key = this.getOption('labelKey') || 'name'
// Variables mode.
if (labelKey) {
if (Utils.hasVar(labelKey)) {
return Utils.greedyTemplate(labelKey, this.extendedProperties())
}
keys.unshift(labelKey)
if (Utils.hasVar(key)) {
return Utils.greedyTemplate(key, this.extendedProperties())
}
for (const key of keys) {
const value = this.properties[key]
if (value) return value
}
return this.datalayer.getName()
// Simple mode.
return (
this.properties[key] ||
this.properties.title ||
fallback ||
this.datalayer.getName()
)
}
hasPopupFooter() {

View file

@ -289,7 +289,7 @@ export class DataLayer extends ServerStored {
reindex() {
const features = Object.values(this._features)
this.sortFeatures(features)
Utils.sortFeatures(features, this._umap.getProperty('sortKey'), U.lang)
this._index = features.map((feature) => stamp(feature))
}
@ -428,10 +428,6 @@ export class DataLayer extends ServerStored {
if (idx !== -1) this._propertiesIndex.splice(idx, 1)
}
allProperties() {
return this._propertiesIndex
}
sortedValues(property) {
return Object.values(this._features)
.map((feature) => feature.properties[property])
@ -450,11 +446,6 @@ export class DataLayer extends ServerStored {
}
}
sortFeatures(collection) {
const sortKeys = this._umap.getProperty('sortKey') || U.DEFAULT_LABEL_KEY
return Utils.sortFeatures(collection, sortKeys, U.lang)
}
makeFeatures(geojson = {}, sync = true) {
if (geojson.type === 'Feature' || geojson.coordinates) {
geojson = [geojson]
@ -463,7 +454,7 @@ export class DataLayer extends ServerStored {
? geojson
: geojson.features || geojson.geometries
if (!collection) return
this.sortFeatures(collection)
Utils.sortFeatures(collection, this._umap.getProperty('sortKey'), U.lang)
for (const feature of collection) {
this.makeFeature(feature, sync)
}

View file

@ -1,5 +1,5 @@
import { uMapAlert as Alert } from '../components/alerts/alert.js'
import { AjaxAutocomplete, AjaxAutocompleteMultiple, AutocompleteDatalist } from './autocomplete.js'
import { AjaxAutocomplete, AjaxAutocompleteMultiple } from './autocomplete.js'
import Help from './help.js'
import { ServerRequest } from './request.js'
import { SCHEMA } from './schema.js'
@ -17,7 +17,6 @@ window.U = {
Alert,
AjaxAutocomplete,
AjaxAutocompleteMultiple,
AutocompleteDatalist,
Help,
Icon,
LAYER_TYPES,

View file

@ -33,15 +33,15 @@ const TEMPLATE = `
<fieldset id="import-mode" class="formbox">
<legend class="counter" data-help="importMode">${translate('Choose import mode')}</legend>
<label>
<input type="radio" name="action" value="copy" checked onchange />
<input type="radio" name="action" value="copy" />
${translate('Copy into the layer')}
</label>
<label>
<input type="radio" name="action" value="link" onchange />
<input type="radio" name="action" value="link" />
${translate('Link to the layer as remote data')}
</label>
</fieldset>
<input type="button" class="button primary" name="submit" value="${translate('Import data')}" disabled />
<input type="button" class="button" name="submit" value="${translate('Import data')}" />
</div>
`
@ -121,11 +121,6 @@ export default class Importer extends Utils.WithTemplate {
return this.qs('textarea').value
}
set raw(value) {
this.qs('textarea').value = value
this.onChange()
}
get clear() {
return Boolean(this.qs('[name=clear]').checked)
}
@ -203,7 +198,6 @@ export default class Importer extends Utils.WithTemplate {
)
this.qs('[name=layer-name]').toggleAttribute('hidden', Boolean(this.layerId))
this.qs('#clear').toggleAttribute('hidden', !this.layerId)
this.qs('[name=submit').toggleAttribute('disabled', !this.canSubmit())
}
onFileChange(e) {
@ -225,7 +219,6 @@ export default class Importer extends Utils.WithTemplate {
this.url = null
this.format = undefined
this.layerName = null
this.raw = null
const layerSelect = this.qs('[name="layer-id"]')
layerSelect.innerHTML = ''
this._umap.eachDataLayerReverse((datalayer) => {
@ -258,17 +251,6 @@ export default class Importer extends Utils.WithTemplate {
this.qs('[type=file]').showPicker()
}
canSubmit() {
if (!this.format) return false
const hasFiles = Boolean(this.files.length)
const hasRaw = Boolean(this.raw)
const hasUrl = Boolean(this.url)
const hasAction = Boolean(this.action)
if (!hasFiles && !hasRaw && !hasUrl) return false
if (this.url) return hasAction
return true
}
submit() {
let hasErrors
if (this.format === 'umap') {

View file

@ -2,8 +2,6 @@ import { DomUtil } from '../../../vendors/leaflet/leaflet-src.esm.js'
import { BaseAjax, SingleMixin } from '../autocomplete.js'
import * as Util from '../utils.js'
import { AutocompleteCommunes } from './communesfr.js'
import { translate } from '../i18n.js'
import { uMapAlert as Alert } from '../../components/alerts/alert.js'
const TEMPLATE = `
<h3>Cadastre</h3>
@ -58,8 +56,6 @@ export class Importer {
.open({
template: container,
className: `${this.id} importer dark`,
cancel: false,
accept: translate('Choose this data'),
})
.then(confirm)
}

View file

@ -37,8 +37,8 @@ class Autocomplete extends SingleMixin(BaseAjax) {
}
export class Importer {
constructor(umap, options = {}) {
this.umap = umap
constructor(map, options = {}) {
this.map = map
this.name = options.name || 'GeoDataMine'
this.baseUrl = options?.url || 'https://geodatamine.fr'
this.id = 'geodatamine'
@ -49,7 +49,7 @@ export class Importer {
let boundaryName = null
const container = DomUtil.create('div')
container.innerHTML = TEMPLATE
const response = await this.umap.request.get(`${this.baseUrl}/themes`)
const response = await importer.map.request.get(`${this.baseUrl}/themes`)
const select = container.querySelector('select')
if (response?.ok) {
const { themes } = await response.json()

View file

@ -53,15 +53,11 @@ export class Importer {
'https://photon.komoot.io/api?q={q}&layer=county&layer=city&layer=state'
this.id = 'overpass'
this.boundaryChoice = null
this.expression = null
}
async open(importer) {
const container = DomUtil.create('div')
container.innerHTML = TEMPLATE
if (this.expression) {
container.querySelector('[name=tags]').value = this.expression
}
this.autocomplete = new Autocomplete(container.querySelector('#area'), {
url: this.searchUrl,
placeholder: translate(
@ -84,7 +80,6 @@ export class Importer {
Alert.error(translate('Expression is empty'))
return
}
this.expression = form.tags
let tags = form.tags
if (!tags.startsWith('[')) tags = `[${tags}]`
let area = '{south},{west},{north},{east}'

View file

@ -44,12 +44,12 @@ const ControlsMixin = {
new U.DrawToolbar({ map: this }).addTo(this)
const editActions = [
U.EditCaptionAction,
U.ImportAction,
U.EditPropertiesAction,
U.EditLayersAction,
U.ChangeTileLayerAction,
U.UpdateExtentAction,
U.UpdatePermsAction,
U.EditPropertiesAction,
U.ImportAction,
]
if (this.options.editMode === 'advanced') {
new U.SettingsToolbar({ actions: editActions }).addTo(this)

View file

@ -115,9 +115,7 @@ class Table extends TitleMixin(PopupTemplate) {
const table = document.createElement('table')
for (const key in feature.properties) {
if (typeof feature.properties[key] === 'object' || U.LABEL_KEYS.includes(key)) {
continue
}
if (typeof feature.properties[key] === 'object' || key === 'name') continue
table.appendChild(this.makeRow(feature, key))
}
return table
@ -152,19 +150,6 @@ class GeoRSSLink extends PopupTemplate {
}
class OSM extends TitleMixin(PopupTemplate) {
renderTitle(feature) {
const title = DomUtil.add('h3', 'popup-title')
const color = feature.getPreviewColor()
title.style.backgroundColor = color
const iconUrl = feature.getDynamicOption('iconUrl')
const icon = Icon.makeElement(iconUrl, title)
DomUtil.addClass(icon, 'icon')
Icon.setContrast(icon, title, iconUrl, color)
if (DomUtil.contrastedColor(title, color)) title.style.color = 'white'
DomUtil.add('span', '', title, this.getName(feature))
return title
}
getName(feature) {
const props = feature.properties
const locale = getLocale()
@ -175,6 +160,15 @@ class OSM extends TitleMixin(PopupTemplate) {
renderBody(feature) {
const props = feature.properties
const body = document.createElement('div')
const title = DomUtil.add('h3', 'popup-title', container)
const color = feature.getPreviewColor()
title.style.backgroundColor = color
const iconUrl = feature.getDynamicOption('iconUrl')
const icon = Icon.makeElement(iconUrl, title)
DomUtil.addClass(icon, 'icon')
Icon.setContrast(icon, title, iconUrl, color)
if (DomUtil.contrastedColor(title, color)) title.style.color = 'white'
DomUtil.add('span', '', title, this.getName(feature))
const street = props['addr:street']
if (street) {
const row = DomUtil.add('address', 'address', body)
@ -211,13 +205,6 @@ class OSM extends TitleMixin(PopupTemplate) {
Utils.loadTemplate(`<div><a href="mailto:${email}">${email}</a></div>`)
)
}
if (props.panoramax) {
body.appendChild(
Utils.loadTemplate(
`<div><img src="https://api.panoramax.xyz/api/pictures/${props.panoramax}/sd.jpg" /></div>`
)
)
}
const id = props['@id'] || props.id
if (id) {
body.appendChild(

View file

@ -75,7 +75,7 @@ const FeatureMixin = {
resetTooltip: function () {
if (!this.feature.hasGeom()) return
const displayName = this.feature.getDisplayName()
const displayName = this.feature.getDisplayName(null)
let showLabel = this.feature.getOption('showLabel')
const oldLabelHover = this.feature.getOption('labelHover')
@ -255,10 +255,7 @@ const PathMixin = {
if (this._map.measureTools?.enabled()) {
this._map._umap.tooltip.open({ content: this.getMeasure(), anchor: this })
} else if (this._map._umap.editEnabled && !this._map._umap.editedFeature) {
this._map._umap.tooltip.open({
content: translate('Click to edit'),
anchor: this,
})
this._map._umap.tooltip.open({ content: translate('Click to edit'), anchor: this })
}
},
@ -270,9 +267,7 @@ const PathMixin = {
this._map.once('moveend', this.makeGeometryEditable, this)
const pointsCount = this._parts.reduce((acc, part) => acc + part.length, 0)
if (pointsCount > 100 && this._map.getZoom() < this._map.getMaxZoom()) {
this._map._umap.tooltip.open({
content: L._('Please zoom in to edit the geometry'),
})
this._map._umap.tooltip.open({ content: L._('Please zoom in to edit the geometry') })
this.disableEdit()
} else {
this.enableEdit()
@ -385,19 +380,8 @@ export const LeafletPolyline = Polyline.extend({
},
getMeasure: function (shape) {
let shapes
if (shape) {
shapes = [shape]
} else if (LineUtil.isFlat(this._latlngs)) {
shapes = [this._latlngs]
} else {
shapes = this._latlngs
}
// FIXME: compute from data in feature (with TurfJS)
const length = shapes.reduce(
(acc, shape) => acc + L.GeoUtil.lineLength(this._map, shape),
0
)
const length = L.GeoUtil.lineLength(this._map, shape || this._defaultShape())
return L.GeoUtil.readableDistance(length, this._map.measureTools.getMeasureUnit())
},
})

View file

@ -105,7 +105,7 @@ export default class TableEditor extends WithTemplate {
resetProperties() {
this.properties = this.datalayer._propertiesIndex
if (this.properties.length === 0) {
this.properties = [U.DEFAULT_LABEL_KEY, 'description']
this.properties = ['name', 'description']
}
}

View file

@ -71,10 +71,6 @@ export default class Umap extends ServerStored {
// To be used in javascript APIs
if (geojson.properties.lang) U.lang = geojson.properties.lang
// Make it available to utils, without needing a reference to `Umap`.
U.LABEL_KEYS = geojson.properties.defaultLabelKeys || []
U.DEFAULT_LABEL_KEY = U.LABEL_KEYS[0] || 'name'
this.setPropertiesFromQueryString()
// Needed for actions labels
@ -541,10 +537,10 @@ export default class Umap extends ServerStored {
this._leafletMap.editTools.startPolyline()
break
case 'i':
this.importer.open()
this._leafletMap.importer.open()
break
case 'o':
this.importer.openFiles()
this._leafletMap.importer.openFiles()
break
case 'h':
this.help.showGetStarted()
@ -600,7 +596,7 @@ export default class Umap extends ServerStored {
const panes = this._leafletMap.getPane('overlayPane')
this.datalayersIndex = []
for (const pane of panes.children) {
for (const pane of panes) {
if (!pane.dataset || !pane.dataset.id) continue
this.datalayersIndex.push(this.datalayers[pane.dataset.id])
}
@ -688,7 +684,7 @@ export default class Umap extends ServerStored {
}
allProperties() {
return [].concat(...this.datalayersIndex.map((dl) => dl.allProperties()))
return [].concat(...this.datalayersIndex.map((dl) => dl._propertiesIndex))
}
sortedValues(property) {
@ -1429,13 +1425,13 @@ export default class Umap extends ServerStored {
row.dataset.id = stamp(datalayer)
})
const onReorder = (src, dst, initialIndex, finalIndex) => {
const movedLayer = this.datalayers[src.dataset.id]
const targetLayer = this.datalayers[dst.dataset.id]
const minIndex = Math.min(movedLayer.getRank(), targetLayer.getRank())
const maxIndex = Math.max(movedLayer.getRank(), targetLayer.getRank())
if (finalIndex === 0) movedLayer.bringToTop()
else if (finalIndex > initialIndex) movedLayer.insertBefore(targetLayer)
else movedLayer.insertAfter(targetLayer)
const layer = this.datalayers[src.dataset.id]
const other = this.datalayers[dst.dataset.id]
const minIndex = Math.min(layer.getRank(), other.getRank())
const maxIndex = Math.max(layer.getRank(), other.getRank())
if (finalIndex === 0) layer.bringToTop()
else if (finalIndex > initialIndex) layer.insertBefore(other)
else layer.insertAfter(other)
this.eachDataLayerReverse((datalayer) => {
if (datalayer.getRank() >= minIndex && datalayer.getRank() <= maxIndex)
datalayer.isDirty = true

View file

@ -292,7 +292,7 @@ export function naturalSort(a, b, lang) {
}
export function sortFeatures(features, sortKey, lang) {
const sortKeys = sortKey.split(',')
const sortKeys = (sortKey || 'name').split(',')
const sort = (a, b, i) => {
let sortKey = sortKeys[i]

View file

@ -170,43 +170,22 @@ L.DomUtil.contrastWCAG21 = (rgb) => {
const contrast = (whiteLum + 0.05) / (lum + 0.05)
return contrast > 3 ? 1 : 0
}
L.DomUtil.colorNameToHex = (str) => {
const ctx = document.createElement('canvas').getContext('2d')
ctx.fillStyle = str
return ctx.fillStyle
}
L.DomUtil.hexToRGB = (hex) => {
return hex
.replace(
/^#?([a-f\d])([a-f\d])([a-f\d])$/i,
(m, r, g, b) => `#${r}${r}${g}${g}${b}${b}`
)
.substring(1)
.match(/.{2}/g)
.map((x) => Number.parseInt(x, 16))
}
const _CACHE_CONSTRAST = {}
L.DomUtil.contrastedColor = (el, bgcolor) => {
// Return 0 for black and 1 for white
// bgcolor is a human color, it can be a any keyword (purple…)
if (typeof _CACHE_CONSTRAST[bgcolor] !== 'undefined') return _CACHE_CONSTRAST[bgcolor]
let out = 0
let rgb = window.getComputedStyle(el).getPropertyValue('background-color')
rgb = L.DomUtil.RGBRegex.exec(rgb)
if (rgb && rgb.length === 4) {
rgb = [
Number.parseInt(rgb[1], 10),
Number.parseInt(rgb[2], 10),
Number.parseInt(rgb[3], 10),
]
} else {
// The element may not yet be added to the DOM, so let's try
// another way
const hex = L.DomUtil.colorNameToHex(bgcolor)
rgb = L.DomUtil.hexToRGB(hex)
}
if (!rgb) return 1
const out = L.DomUtil.contrastWCAG21(rgb)
if (!rgb || rgb.length !== 4) return out
rgb = [
Number.parseInt(rgb[1], 10),
Number.parseInt(rgb[2], 10),
Number.parseInt(rgb[3], 10),
]
out = L.DomUtil.contrastWCAG21(rgb)
if (bgcolor) _CACHE_CONSTRAST[bgcolor] = out
return out
}

View file

@ -448,17 +448,6 @@ L.FormBuilder.BlurInput.include({
},
})
// Adds an autocomplete using all available user defined properties
L.FormBuilder.PropertyInput = L.FormBuilder.BlurInput.extend({
build: function () {
L.FormBuilder.BlurInput.prototype.build.call(this)
const autocomplete = new U.AutocompleteDatalist(this.input)
// Will be used on Umap and DataLayer
const properties = this.builder.obj.allProperties()
autocomplete.suggestions = properties
},
})
L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
type: () => 'hidden',
@ -1121,11 +1110,10 @@ U.FormBuilder = L.FormBuilder.extend({
},
customHandlers: {
sortKey: 'PropertyInput',
sortKey: 'BlurInput',
easing: 'Switch',
facetKey: 'PropertyInput',
slugKey: 'PropertyInput',
labelKey: 'PropertyInput',
facetKey: 'BlurInput',
slugKey: 'BlurInput',
},
computeDefaultOptions: function () {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View file

@ -15,12 +15,7 @@ DATALAYER_DATA = {
"features": [
{
"type": "Feature",
"properties": {
"name": "one point in france",
"foo": "point",
"bar": "one",
"label": "this is label one",
},
"properties": {"name": "one point in france", "foo": "point", "bar": "one"},
"geometry": {"type": "Point", "coordinates": [3.339844, 46.920255]},
},
{
@ -29,7 +24,6 @@ DATALAYER_DATA = {
"name": "one polygon in greenland",
"foo": "polygon",
"bar": "two",
"label": "this is label two",
},
"geometry": {
"type": "Polygon",
@ -50,7 +44,6 @@ DATALAYER_DATA = {
"name": "one line in new zeland",
"foo": "line",
"bar": "three",
"label": "this is label three",
},
"geometry": {
"type": "LineString",
@ -483,11 +476,3 @@ def test_main_toolbox_toggle_all_layers(live_server, map, page):
# Should hidden again all layers
expect(page.locator(".datalayer.off")).to_have_count(3)
expect(markers).to_have_count(0)
def test_honour_the_label_fields_settings(live_server, map, page, bootstrap, settings):
settings.UMAP_LABEL_KEYS = ["label", "name"]
page.goto(f"{live_server.url}{map.get_absolute_url()}")
expect(page.locator(".panel").get_by_text("this is label one")).to_be_visible()
expect(page.locator(".panel").get_by_text("this is label two")).to_be_visible()
expect(page.locator(".panel").get_by_text("this is label three")).to_be_visible()

View file

@ -1,44 +0,0 @@
import pytest
from playwright.sync_api import expect
from ..base import DataLayerFactory
pytestmark = pytest.mark.django_db
OSM_DATA = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [2.49, 48.79]},
"properties": {
"amenity": "restaurant",
"cuisine": "italian",
"name": "A Casa di Nonna",
"panoramax": "d811b398-d930-4cf8-95a2-0c29c34d9fca",
"phone": "+33 1 48 89 54 12",
"takeaway:covid19": "yes",
"wheelchair": "no",
"id": "node/1130849864",
},
"id": "AzMjk",
},
],
"_umap_options": {
"popupTemplate": "OSM",
},
}
def test_openstreetmap_popup(live_server, map, page):
DataLayerFactory(map=map, data=OSM_DATA)
page.goto(f"{live_server.url}{map.get_absolute_url()}#18/48.79/2.49")
expect(page.locator(".umap-icon-active")).to_be_hidden()
page.locator(".leaflet-marker-icon").click()
expect(page.get_by_role("heading", name="A Casa di Nonna")).to_be_visible()
expect(page.get_by_text("+33 1 48 89 54 12")).to_be_visible()
img = page.locator(".umap-popup-content img")
expect(img).to_have_attribute(
"src",
"https://api.panoramax.xyz/api/pictures/d811b398-d930-4cf8-95a2-0c29c34d9fca/sd.jpg",
)

View file

@ -49,37 +49,3 @@ def test_should_open_popup_on_click(live_server, map, page, bootstrap):
# Close popup
page.locator("#map").click()
expect(line).to_have_attribute("stroke-opacity", "0.5")
def test_can_use_measure_on_name(live_server, map, page):
data = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {"name": "linestring"},
"geometry": {
"type": "LineString",
"coordinates": [
[11.25, 53.585984],
[10.151367, 52.975108],
],
},
},
{
"type": "Feature",
"properties": {"name": "multilinestring"},
"geometry": {
"type": "MultiLineString",
"coordinates": [[[8, 53], [13, 52]], [[12, 51], [15, 52]]],
},
},
],
}
map.settings["properties"]["labelKey"] = "{name} ({measure})"
map.settings["properties"]["onLoadPanel"] = "databrowser"
map.save()
DataLayerFactory(map=map, data=data)
page.goto(f"{live_server.url}{map.get_absolute_url()}#6/10/50")
expect(page.get_by_text("linestring (99.7 km)")).to_be_visible()
expect(page.get_by_text("multilinestring (592 km)")).to_be_visible()

View file

@ -609,7 +609,6 @@ class MapDetailMixin(SessionMixin):
"websocketEnabled": settings.WEBSOCKET_ENABLED,
"websocketURI": settings.WEBSOCKET_FRONT_URI,
"importers": settings.UMAP_IMPORTERS,
"defaultLabelKeys": settings.UMAP_LABEL_KEYS,
}
created = bool(getattr(self, "object", None))
if (created and self.object.owner) or (not created and not user.is_anonymous):