forked from MapComplete/MapComplete
Merge branch 'master' of source.mapcomplete.org:MapComplete/MapComplete
This commit is contained in:
commit
6c8c4a3cf3
12 changed files with 117 additions and 37 deletions
|
@ -4,6 +4,10 @@
|
||||||
"type": "feat",
|
"type": "feat",
|
||||||
"section": "Features"
|
"section": "Features"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "feature",
|
||||||
|
"section": "Features"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "fix",
|
"type": "fix",
|
||||||
"section": "Bug Fixes"
|
"section": "Bug Fixes"
|
||||||
|
@ -51,4 +55,4 @@
|
||||||
],
|
],
|
||||||
"commitUrlFormat": "https://source.mapcomplete.org/MapComplete/MapComplete/commits/{{hash}}",
|
"commitUrlFormat": "https://source.mapcomplete.org/MapComplete/MapComplete/commits/{{hash}}",
|
||||||
"compareUrlFormat": "https://source.mapcomplete.org/MapComplete/MapComplete/compare/{{previousTag}}...{{currentTag}}"
|
"compareUrlFormat": "https://source.mapcomplete.org/MapComplete/MapComplete/compare/{{previousTag}}...{{currentTag}}"
|
||||||
}
|
}
|
||||||
|
|
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -2,6 +2,19 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
### [0.51.1](https://source.mapcomplete.org/MapComplete/MapComplete/compare/v0.51.0...v0.51.1) (2025-04-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* actually upload images to notes ([a870724](https://source.mapcomplete.org/MapComplete/MapComplete/commits/a8707243cf186846ead4a02fb9e62d24f91e14e1))
|
||||||
|
|
||||||
|
|
||||||
|
### Theme improvements
|
||||||
|
|
||||||
|
* **mobility_hub:** Add examples and show sign type ([b5a365d](https://source.mapcomplete.org/MapComplete/MapComplete/commits/b5a365d3c31ac95c6042161288918d1d725323ad))
|
||||||
|
* **mobility_hub:** Add question about physical marker ([f59cf14](https://source.mapcomplete.org/MapComplete/MapComplete/commits/f59cf1430af7a0722a553b7bceaac589db3dab6d))
|
||||||
|
|
||||||
## [0.51.0](https://source.mapcomplete.org/MapComplete/MapComplete/compare/v0.50.8...v0.51.0) (2025-04-15)
|
## [0.51.0](https://source.mapcomplete.org/MapComplete/MapComplete/compare/v0.50.8...v0.51.0) (2025-04-15)
|
||||||
|
|
||||||
### [0.50.8](https://source.mapcomplete.org/MapComplete/MapComplete/compare/v0.50.7...v0.50.8) (2025-04-15)
|
### [0.50.8](https://source.mapcomplete.org/MapComplete/MapComplete/compare/v0.50.7...v0.50.8) (2025-04-15)
|
||||||
|
|
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -1,11 +1,11 @@
|
||||||
"name": "mapcomplete",
|
"name": "mapcomplete",
|
||||||
"version": "0.51.0",
|
"version": "0.51.1",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "mapcomplete",
|
"name": "mapcomplete",
|
||||||
"version": "0.51.0",
|
"version": "0.51.1",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "GPL-3.0-or-later",
|
"license": "GPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "mapcomplete",
|
"name": "mapcomplete",
|
||||||
"version": "0.51.0",
|
"version": "0.51.1",
|
||||||
"repository": "https://source.mapcomplete.org/MapComplete/MapComplete",
|
"repository": "https://source.mapcomplete.org/MapComplete/MapComplete",
|
||||||
"description": "A small website to edit OSM easily",
|
"description": "A small website to edit OSM easily",
|
||||||
"bugs": "hhttps://source.mapcomplete.org/MapComplete/MapComplete/issues",
|
"bugs": "hhttps://source.mapcomplete.org/MapComplete/MapComplete/issues",
|
||||||
|
|
|
@ -212,7 +212,7 @@ export class ImageUploadManager {
|
||||||
args.featureId
|
args.featureId
|
||||||
)
|
)
|
||||||
|
|
||||||
if (args.featureId.startsWith("note/")) {
|
if (!isNaN(Number(args.featureId))) {
|
||||||
// This is an OSM-note
|
// This is an OSM-note
|
||||||
const url = result.absoluteUrl
|
const url = result.absoluteUrl
|
||||||
await this._osmConnection.addCommentToNote(args.featureId, url)
|
await this._osmConnection.addCommentToNote(args.featureId, url)
|
||||||
|
|
|
@ -330,6 +330,9 @@ ${nds}${tags} </way>
|
||||||
}
|
}
|
||||||
|
|
||||||
private isPolygon(): boolean {
|
private isPolygon(): boolean {
|
||||||
|
if (this.coordinates.length === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
// Compare lat and lon seperately, as the coordinate array might not be a reference to the same object
|
// Compare lat and lon seperately, as the coordinate array might not be a reference to the same object
|
||||||
if (
|
if (
|
||||||
this.coordinates[0][0] !== this.coordinates[this.coordinates.length - 1][0] ||
|
this.coordinates[0][0] !== this.coordinates[this.coordinates.length - 1][0] ||
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
import Tr from "../Base/Tr.svelte"
|
import Tr from "../Base/Tr.svelte"
|
||||||
import AccordionSingle from "../Flowbite/AccordionSingle.svelte"
|
import AccordionSingle from "../Flowbite/AccordionSingle.svelte"
|
||||||
import Translations from "../i18n/Translations"
|
import Translations from "../i18n/Translations"
|
||||||
|
import TagRenderingChart from "../BigComponents/TagRenderingChart"
|
||||||
|
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||||
|
import type { TagRenderingConfigJson } from "../../Models/ThemeConfig/Json/TagRenderingConfigJson"
|
||||||
|
|
||||||
export let onlyShowUsername: string[]
|
export let onlyShowUsername: string[]
|
||||||
export let features: Feature[]
|
export let features: Feature[]
|
||||||
|
@ -25,10 +28,11 @@
|
||||||
key: string
|
key: string
|
||||||
value?: string
|
value?: string
|
||||||
oldValue?: string
|
oldValue?: string
|
||||||
|
step: OsmObject
|
||||||
}[]
|
}[]
|
||||||
> = allHistories.mapD((histories) => HistoryUtils.fullHistoryDiff(histories, usernames))
|
> = allHistories.mapD((histories) => HistoryUtils.fullHistoryDiff(histories, usernames))
|
||||||
|
|
||||||
const trs = shared_questions.tagRenderings.map((tr) => new TagRenderingConfig(tr))
|
const trs = shared_questions.tagRenderings.map((tr) => new TagRenderingConfig(<TagRenderingConfigJson>tr))
|
||||||
|
|
||||||
function detectQuestion(key: string): TagRenderingConfig {
|
function detectQuestion(key: string): TagRenderingConfig {
|
||||||
return trs.find((tr) => tr.freeform?.key === key)
|
return trs.find((tr) => tr.freeform?.key === key)
|
||||||
|
@ -40,17 +44,19 @@
|
||||||
tr: TagRenderingConfig
|
tr: TagRenderingConfig
|
||||||
count: number
|
count: number
|
||||||
values: { value: string; count: number }[]
|
values: { value: string; count: number }[]
|
||||||
|
features: Feature[]
|
||||||
}[]
|
}[]
|
||||||
> = allDiffs.mapD((allDiffs) => {
|
> = allDiffs.mapD((allDiffs) => {
|
||||||
const keyCounts = new Map<string, Map<string, number>>()
|
const keyCounts = new Map<string, Map<string, OsmObject[]>>()
|
||||||
for (const diff of allDiffs) {
|
for (const diff of allDiffs) {
|
||||||
const k = diff.key
|
const k = diff.key
|
||||||
if (!keyCounts.has(k)) {
|
if (!keyCounts.has(k)) {
|
||||||
keyCounts.set(k, new Map<string, number>())
|
keyCounts.set(k, new Map<string, OsmObject[]>())
|
||||||
}
|
}
|
||||||
const valueCounts = keyCounts.get(k)
|
const valueCounts = keyCounts.get(k)
|
||||||
const v = diff.value ?? ""
|
const v = diff.value ?? ""
|
||||||
valueCounts.set(v, 1 + (valueCounts.get(v) ?? 0))
|
const oldFeaturesList = valueCounts.get(v) ?? []
|
||||||
|
valueCounts.set(v, [...oldFeaturesList, diff.step])
|
||||||
}
|
}
|
||||||
|
|
||||||
const perKey: {
|
const perKey: {
|
||||||
|
@ -58,19 +64,25 @@
|
||||||
tr: TagRenderingConfig
|
tr: TagRenderingConfig
|
||||||
count: number
|
count: number
|
||||||
values: { value: string; count: number }[]
|
values: { value: string; count: number }[]
|
||||||
|
features: Feature[]
|
||||||
}[] = []
|
}[] = []
|
||||||
keyCounts.forEach((values, key) => {
|
keyCounts.forEach((values: Map<string, OsmObject[]>, key: string) => {
|
||||||
const keyTotal: { value: string; count: number }[] = []
|
const keyTotal: { value: string; features: Feature[] }[] = []
|
||||||
values.forEach((count, value) => {
|
values.forEach((count, value) => {
|
||||||
keyTotal.push({ value, count })
|
keyTotal.push({ value, features: count.map(step => step.asGeoJson()) })
|
||||||
})
|
})
|
||||||
let countForKey = 0
|
let countForKey: Feature[] = []
|
||||||
for (const { count } of keyTotal) {
|
for (const { features } of keyTotal) {
|
||||||
countForKey += count
|
countForKey.push(...features)
|
||||||
}
|
}
|
||||||
keyTotal.sort((a, b) => b.count - a.count)
|
keyTotal.sort((a, b) => b.features.length - a.features.length)
|
||||||
const tr = detectQuestion(key)
|
const tr = detectQuestion(key)
|
||||||
perKey.push({ count: countForKey, tr, key, values: keyTotal })
|
perKey.push({
|
||||||
|
count: countForKey.length, tr, key, values: keyTotal.map(({ value, features }) => ({
|
||||||
|
value,
|
||||||
|
count: features.length
|
||||||
|
})), features: countForKey
|
||||||
|
})
|
||||||
})
|
})
|
||||||
perKey.sort((a, b) => b.count - a.count)
|
perKey.sort((a, b) => b.count - a.count)
|
||||||
|
|
||||||
|
@ -105,6 +117,20 @@
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
|
{#if diff.tr}
|
||||||
|
<div class="w-48 h-48">
|
||||||
|
|
||||||
|
<ToSvelte
|
||||||
|
construct={new TagRenderingChart(diff.features, diff.tr, {
|
||||||
|
groupToOtherCutoff: 0,
|
||||||
|
chartType: "pie",
|
||||||
|
sort: true,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
Could not create a graph
|
||||||
|
{/if}
|
||||||
</AccordionSingle>
|
</AccordionSingle>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -44,22 +44,37 @@ export class HistoryUtils {
|
||||||
.filter((ch) => ch.oldValue !== ch.value)
|
.filter((ch) => ch.oldValue !== ch.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static fullHistoryDiff(histories: OsmObject[][], onlyShowUsername?: Set<string>) {
|
public static fullHistoryDiff(histories: OsmObject[][], onlyShowUsername?: Set<string>): {
|
||||||
const allDiffs: { key: string; oldValue?: string; value?: string }[] = [].concat(
|
key: string;
|
||||||
...histories.map((history) => {
|
value?: string;
|
||||||
const filtered = history.filter(
|
oldValue?: string;
|
||||||
(step) =>
|
step: OsmObject
|
||||||
!onlyShowUsername ||
|
}[] {
|
||||||
onlyShowUsername?.has(step.tags["_last_edit:contributor"])
|
|
||||||
)
|
const allDiffs: {
|
||||||
const diffs: {
|
key: string;
|
||||||
key: string
|
value?: string;
|
||||||
value?: string
|
oldValue?: string;
|
||||||
oldValue?: string
|
step: OsmObject
|
||||||
}[][] = filtered.map((step) => HistoryUtils.tagHistoryDiff(step, history))
|
}[] = []
|
||||||
return [].concat(...diffs)
|
for (const history of histories) {
|
||||||
})
|
const filtered = history.filter(
|
||||||
)
|
(step) =>
|
||||||
|
!onlyShowUsername ||
|
||||||
|
onlyShowUsername?.has(step.tags["_last_edit:contributor"])
|
||||||
|
)
|
||||||
|
for (const step of filtered) {
|
||||||
|
const diff: {
|
||||||
|
key: string;
|
||||||
|
value?: string;
|
||||||
|
oldValue?: string;
|
||||||
|
step: OsmObject
|
||||||
|
}[] = HistoryUtils.tagHistoryDiff(step, history)
|
||||||
|
allDiffs.push(...diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return allDiffs
|
return allDiffs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
import FileSelector from "../Base/FileSelector.svelte"
|
import FileSelector from "../Base/FileSelector.svelte"
|
||||||
import LoginButton from "../Base/LoginButton.svelte"
|
import LoginButton from "../Base/LoginButton.svelte"
|
||||||
import { Translation } from "../i18n/Translation"
|
import { Translation } from "../i18n/Translation"
|
||||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
|
||||||
import type { Feature } from "geojson"
|
import type { Feature } from "geojson"
|
||||||
import Camera from "@babeard/svelte-heroicons/mini/Camera"
|
import Camera from "@babeard/svelte-heroicons/mini/Camera"
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,8 @@
|
||||||
searchIsRunning.set(false)
|
searchIsRunning.set(false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
let state = {
|
let state = {
|
||||||
mapProperties: maplibremap,
|
mapProperties: maplibremap,
|
||||||
searchState: {
|
searchState: {
|
||||||
|
@ -164,6 +166,19 @@
|
||||||
|
|
||||||
let showPreviouslyVisited = new UIEventSource(true)
|
let showPreviouslyVisited = new UIEventSource(true)
|
||||||
const t = Translations.t.inspector
|
const t = Translations.t.inspector
|
||||||
|
|
||||||
|
function search(suggestion?: GeocodeResult) {
|
||||||
|
suggestion ??= searchSuggestions?.data?.[0]
|
||||||
|
console.log("Seaching", suggestion)
|
||||||
|
if (!suggestion) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
map.data.flyTo({
|
||||||
|
zoom: 14,
|
||||||
|
center: [suggestion.lon, suggestion.lat]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex h-screen w-full flex-col">
|
<div class="flex h-screen w-full flex-col">
|
||||||
|
@ -251,12 +266,13 @@
|
||||||
<MaplibreMap {map} mapProperties={maplibremap} autorecovery={true} />
|
<MaplibreMap {map} mapProperties={maplibremap} autorecovery={true} />
|
||||||
<div class="absolute right-0 top-0 w-1/4 p-4">
|
<div class="absolute right-0 top-0 w-1/4 p-4">
|
||||||
<Searchbar
|
<Searchbar
|
||||||
|
on:search={() => search()}
|
||||||
isFocused={searchIsFocussed}
|
isFocused={searchIsFocussed}
|
||||||
value={searchvalue}
|
value={searchvalue}
|
||||||
on:focus={() => state.searchState.showSearchDrawer.set(true)}
|
on:focus={() => state.searchState.showSearchDrawer.set(true)}
|
||||||
/>
|
/>
|
||||||
{#if $searchSuggestions?.length > 0 || $searchIsFocussed}
|
{#if $searchSuggestions?.length > 0 || $searchIsFocussed}
|
||||||
<GeocodeResults {state} />
|
<GeocodeResults {state} on:select={(event) => search(event.detail)} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
import FeaturePropertiesStore from "../../Logic/FeatureSource/Actors/FeaturePropertiesStore"
|
import FeaturePropertiesStore from "../../Logic/FeatureSource/Actors/FeaturePropertiesStore"
|
||||||
import SearchState from "../../Logic/State/SearchState"
|
import SearchState from "../../Logic/State/SearchState"
|
||||||
import ArrowUp from "@babeard/svelte-heroicons/mini/ArrowUp"
|
import ArrowUp from "@babeard/svelte-heroicons/mini/ArrowUp"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
export let entry: GeocodeResult
|
export let entry: GeocodeResult
|
||||||
export let state: {
|
export let state: {
|
||||||
|
@ -40,8 +41,9 @@
|
||||||
let mapRotation = state.mapProperties.rotation
|
let mapRotation = state.mapProperties.rotation
|
||||||
let inView = state.mapProperties.bounds.mapD((bounds) => bounds.contains([entry.lon, entry.lat]))
|
let inView = state.mapProperties.bounds.mapD((bounds) => bounds.contains([entry.lon, entry.lat]))
|
||||||
|
|
||||||
|
let dispatch = createEventDispatcher<{ select: GeocodeResult }>()
|
||||||
function select() {
|
function select() {
|
||||||
state.searchState.applyGeocodeResult(entry)
|
dispatch("select", entry)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,9 @@
|
||||||
{#if $allowFilters}
|
{#if $allowFilters}
|
||||||
<FilterResults {state} />
|
<FilterResults {state} />
|
||||||
{/if}
|
{/if}
|
||||||
<GeocodeResults {state}>
|
<GeocodeResults {state} on:select={(select) => {
|
||||||
|
state.searchState.applyGeocodeResult(select.detail)
|
||||||
|
}}>
|
||||||
<svelte:fragment slot="if-no-results">
|
<svelte:fragment slot="if-no-results">
|
||||||
{#if $recentlySeen?.length > 0}
|
{#if $recentlySeen?.length > 0}
|
||||||
<SidebarUnit>
|
<SidebarUnit>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue