forked from MapComplete/MapComplete
		
	Add theme introspecting mapcomplete changes
This commit is contained in:
		
							parent
							
								
									ccb548816f
								
							
						
					
					
						commit
						2e2a6965e7
					
				
					 8 changed files with 275 additions and 47 deletions
				
			
		|  | @ -2,7 +2,7 @@ import {Utils} from "../Utils"; | |||
| 
 | ||||
| export default class Constants { | ||||
| 
 | ||||
|     public static vNumber = "0.14.0-alpha-5"; | ||||
|     public static vNumber = "0.14.0-rc-1"; | ||||
|     public static ImgurApiKey = '7070e7167f0a25a' | ||||
|     public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ import Combine from "../Base/Combine"; | |||
| import {FixedUiElement} from "../Base/FixedUiElement"; | ||||
| import {Utils} from "../../Utils"; | ||||
| import BaseUIElement from "../BaseUIElement"; | ||||
| import Translations from "../i18n/Translations"; | ||||
| import Svg from "../../Svg"; | ||||
| 
 | ||||
| export default class Histogram<T> extends VariableUiElement { | ||||
| 
 | ||||
|  | @ -13,9 +13,9 @@ export default class Histogram<T> extends VariableUiElement { | |||
|         "#ff5858", | ||||
|         "#ffad48", | ||||
|         "#ffff59", | ||||
|         "#9d62d9", | ||||
|         "#56bd56", | ||||
|         "#63a9ff", | ||||
|         "#9d62d9", | ||||
|         "#fa61fa" | ||||
|     ] | ||||
| 
 | ||||
|  | @ -24,6 +24,51 @@ export default class Histogram<T> extends VariableUiElement { | |||
|                 countTitle: string | BaseUIElement, | ||||
|                 assignColor?: (t0: string) => string | ||||
|     ) { | ||||
|         const sortMode = new UIEventSource<"name" | "name-rev" | "count" | "count-rev">("name") | ||||
|         const sortName = new VariableUiElement(sortMode.map(m => { | ||||
|             switch (m) { | ||||
|                 case "name": | ||||
|                     return Svg.up_svg() | ||||
|                 case "name-rev": | ||||
|                     return Svg.up_svg().SetStyle("transform: rotate(180deg)") | ||||
|                 default: | ||||
|                     return Svg.circle_svg() | ||||
|             } | ||||
|         })) | ||||
|         const titleHeader = new Combine([sortName.SetClass("w-4 mr-2"), title]).SetClass("flex") | ||||
|             .onClick(() => { | ||||
|                 if (sortMode.data === "name") { | ||||
|                     sortMode.setData("name-rev") | ||||
|                 } else { | ||||
|                     sortMode.setData("name") | ||||
|                 } | ||||
|             }) | ||||
| 
 | ||||
|         const sortCount = new VariableUiElement(sortMode.map(m => { | ||||
|             switch (m) { | ||||
|                 case "count": | ||||
|                     return Svg.up_svg() | ||||
|                 case "count-rev": | ||||
|                     return Svg.up_svg().SetStyle("transform: rotate(180deg)") | ||||
|                 default: | ||||
|                     return Svg.circle_svg() | ||||
|             } | ||||
|         })) | ||||
| 
 | ||||
| 
 | ||||
|         const countHeader = new Combine([sortCount.SetClass("w-4 mr-2"), countTitle]).SetClass("flex").onClick(() => { | ||||
|             if (sortMode.data === "count-rev") { | ||||
|                 sortMode.setData("count") | ||||
|             } else { | ||||
|                 sortMode.setData("count-rev") | ||||
|             } | ||||
|         }) | ||||
| 
 | ||||
|         const header = [ | ||||
|             titleHeader, | ||||
|             countHeader | ||||
|         ] | ||||
| 
 | ||||
|         super(values.map(values => { | ||||
| 
 | ||||
|             if (values === undefined) { | ||||
|  | @ -39,7 +84,21 @@ export default class Histogram<T> extends VariableUiElement { | |||
|             } | ||||
| 
 | ||||
|             const keys = Array.from(counts.keys()); | ||||
| 
 | ||||
|             switch (sortMode.data) { | ||||
|                 case "name": | ||||
|                     keys.sort() | ||||
|                     break; | ||||
|                 case "name-rev": | ||||
|                     keys.sort().reverse() | ||||
|                     break; | ||||
|                 case "count": | ||||
|                     keys.sort((k0, k1) => counts.get(k0) - counts.get(k1)) | ||||
|                     break; | ||||
|                 case "count-rev": | ||||
|                     keys.sort((k0, k1) => counts.get(k1) - counts.get(k0)) | ||||
|                     break; | ||||
|             } | ||||
| 
 | ||||
|             const max = Math.max(...Array.from(counts.values())) | ||||
| 
 | ||||
|  | @ -57,7 +116,7 @@ export default class Histogram<T> extends VariableUiElement { | |||
|             } | ||||
| 
 | ||||
|             return new Table( | ||||
|                 [Translations.W(title), countTitle], | ||||
|                 header, | ||||
|                 keys.map(key => [ | ||||
|                     key, | ||||
|                     new Combine([ | ||||
|  | @ -69,6 +128,6 @@ export default class Histogram<T> extends VariableUiElement { | |||
|                 ]), | ||||
|                 keys.map(_ => ["width: 20%"]) | ||||
|             ).SetClass("w-full"); | ||||
|         })); | ||||
|         }, [sortMode])); | ||||
|     } | ||||
| } | ||||
|  | @ -399,12 +399,12 @@ export default class SpecialVisualizations { | |||
|                         }, | ||||
|                         { | ||||
|                             name: "title", | ||||
|                             doc: "The text to put above the given values column", | ||||
|                             doc: "This text will be placed above the texts (in the first column of the visulasition)", | ||||
|                             defaultValue: "" | ||||
|                         }, | ||||
|                         { | ||||
|                             name: "countHeader", | ||||
|                             doc: "The text to put above the counts", | ||||
|                             doc: "This text will be placed above the bars", | ||||
|                             defaultValue: "" | ||||
|                         }, | ||||
|                         { | ||||
|  |  | |||
							
								
								
									
										9
									
								
								Utils.ts
									
										
									
									
									
								
							
							
						
						
									
										9
									
								
								Utils.ts
									
										
									
									
									
								
							|  | @ -137,14 +137,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be | |||
|     } | ||||
| 
 | ||||
|     public static NoNull<T>(array: T[]): T[] { | ||||
|         const ls: T[] = []; | ||||
|         for (const t of array) { | ||||
|             if (t === undefined || t === null) { | ||||
|                 continue; | ||||
|             } | ||||
|             ls.push(t); | ||||
|         } | ||||
|         return ls; | ||||
|         return array.filter(o => o !== undefined && o !== null) | ||||
|     } | ||||
| 
 | ||||
|     public static Hist(array: string[]): Map<string, number> { | ||||
|  |  | |||
|  | @ -1,3 +1,170 @@ | |||
| { | ||||
|   "id": "mapcomplete-changes", | ||||
|   "title": { | ||||
|     "en": "Changes made with MapComplete" | ||||
|   }, | ||||
|   "shortDescription": { | ||||
|     "en": "Shows changes made by MapComplete" | ||||
|   }, | ||||
|   "description": { | ||||
|     "en": "This maps shows all the changes made with MapComplete" | ||||
|   }, | ||||
|   "language": [ | ||||
|     "en" | ||||
|   ], | ||||
|   "maintainer": "", | ||||
|   "icon": "./assets/svg/logo.svg", | ||||
|   "hideFromOverview": true, | ||||
|   "version": "0", | ||||
|   "startLat": 0, | ||||
|   "startLon": 0, | ||||
|   "startZoom": 1, | ||||
|   "widenFactor": 0.05, | ||||
|   "clustering": false, | ||||
|   "layers": [ | ||||
|     { | ||||
|       "id": "mapcomplete-changes", | ||||
|       "name": { | ||||
|         "en": "Changeset centers" | ||||
|       }, | ||||
|       "minzoom": 0, | ||||
|       "source": { | ||||
|         "osmTags": "editor~*", | ||||
|         "geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/mapcomplete-changes/tile_{z}_{x}_{y}.geojson", | ||||
|         "geoJsonZoomLevel": 8, | ||||
|         "maxCacheAge": 0 | ||||
|       }, | ||||
|       "calculatedTags": [ | ||||
|         "_last_edit:contributor:lc:=feat.properties['_last_edit:contributor'].toLowerCase()" | ||||
|       ], | ||||
|       "title": { | ||||
|         "render": { | ||||
|           "en": "Changeset for {theme}" | ||||
|         } | ||||
|       }, | ||||
|       "description": { | ||||
|         "en": "Shows all MapComplete changes" | ||||
|       }, | ||||
|       "tagRenderings": [ | ||||
|         { | ||||
|           "id": "contributor", | ||||
|           "render": { | ||||
|             "en": "Change made by <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a>" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "id": "theme", | ||||
|           "render": { | ||||
|             "en": "Change with theme <a href='https://mapcomplete.osm.be/{theme}'>{theme}</a>" | ||||
|           }, | ||||
|           "mappings": [ | ||||
|             { | ||||
|               "if": "theme~http.*", | ||||
|               "then": { | ||||
|                 "en": "Change with <b>unofficial</b> theme <a href='https://mapcomplete.osm.be/theme.html?userlayout={theme}'>{theme}</a>" | ||||
|               } | ||||
|             } | ||||
|           ] | ||||
|         } | ||||
|       ], | ||||
|       "mapRendering": [ | ||||
|         { | ||||
|           "location": [ | ||||
|             "point", | ||||
|             "centroid" | ||||
|           ], | ||||
|           "icon": {"render":"teardrop:#00cc00", | ||||
|             "mappings": [{"if":"theme=aed","then":"./assets/themes/aed/logo.svg"},{"if":"theme=aed_brugge","then":"./assets/themes/aed/logo.svg"},{"if":"theme=artwork","then":"./assets/themes/artwork/artwork.svg"},{"if":"theme=benches","then":"./assets/themes/benches/bench_poi.svg"},{"if":"theme=bicyclelib","then":"./assets/themes/bicyclelib/logo.svg"},{"if":"theme=binoculars","then":"./assets/layers/binocular/telescope.svg"},{"if":"theme=bookcases","then":"./assets/themes/bookcases/bookcase.svg"},{"if":"theme=buurtnatuur","then":"./assets/themes/buurtnatuur/groen_logo.svg"},{"if":"theme=cafes_and_pubs","then":"./assets/layers/cafe_pub/pub.svg"},{"if":"theme=campersite","then":"./assets/themes/campersite/caravan.svg"},{"if":"theme=charging_stations","then":"./assets/themes/charging_stations/logo.svg"},{"if":"theme=climbing","then":"./assets/themes/climbing/climbing_icon.svg"},{"if":"theme=cycle_highways","then":"./assets/themes/cycle_highways/fietssnelwegen-logo.svg"},{"if":"theme=cycle_infra","then":"./assets/themes/cycle_infra/cycle-infra.svg"},{"if":"theme=cyclenodes","then":"./assets/themes/cyclenodes/logo.svg"},{"if":"theme=cyclestreets","then":"./assets/themes/cyclestreets/F111.svg"},{"if":"theme=cyclofix","then":"./assets/themes/cyclofix/logo.svg"},{"if":"theme=drinking_water","then":"./assets/themes/drinking_water/logo.svg"},{"if":"theme=entrances","then":"./assets/layers/entrance/door.svg"},{"if":"theme=etymology","then":"./assets/layers/etymology/logo.svg"},{"if":"theme=facadegardens","then":"./assets/themes/facadegardens/geveltuin.svg"},{"if":"theme=food","then":"./assets/layers/food/restaurant.svg"},{"if":"theme=fritures","then":"./assets/themes/fritures/logo.svg"},{"if":"theme=fruit_trees","then":"./assets/themes/fruit_trees/fruit_tree.svg"},{"if":"theme=ghostbikes","then":"./assets/themes/ghostbikes/logo.svg"},{"if":"theme=grb","then":"./assets/themes/grb_import/housenumber_blank.svg"},{"if":"theme=grb_fixme","then":"./assets/svg/bug.svg"},{"if":"theme=missing_streets","then":"./assets/svg/robot.svg"},{"if":"theme=hackerspaces","then":"./assets/themes/hackerspaces/glider.svg"},{"if":"theme=hailhydrant","then":"./assets/themes/hailhydrant/logo.svg"},{"if":"theme=mapcomplete-changes","then":"./assets/svg/logo.svg"},{"if":"theme=maps","then":"./assets/themes/maps/logo.svg"},{"if":"theme=nature","then":"./assets/themes/nature/logo.svg"},{"if":"theme=natuurpunt","then":"./assets/themes/natuurpunt/natuurpunt.png"},{"if":"theme=notes","then":"./assets/svg/resolved.svg"},{"if":"theme=observation_towers","then":"./assets/layers/observation_tower/Tower_observation.svg"},{"if":"theme=openwindpowermap","then":"./assets/themes/openwindpowermap/wind_turbine.svg"},{"if":"theme=parkings","then":"./assets/themes/parkings/parkings.svg"},{"if":"theme=personal","then":"./assets/svg/addSmall.svg"},{"if":"theme=play_forests","then":"./assets/layers/play_forest/icon.svg"},{"if":"theme=playgrounds","then":"./assets/themes/playgrounds/playground.svg"},{"if":"theme=postal_codes","then":"./assets/themes/postal_codes/townhall.svg"},{"if":"theme=postboxes","then":"./assets/themes/postboxes/postbox.svg"},{"if":"theme=shops","then":"./assets/themes/shops/shop.svg"},{"if":"theme=sidewalks","then":"./assets/svg/bug.svg"},{"if":"theme=speelplekken","then":"./assets/themes/speelplekken/logo.svg"},{"if":"theme=sport_pitches","then":"./assets/layers/sport_pitch/table_tennis.svg"},{"if":"theme=street_lighting","then":"./assets/layers/street_lamps/street_lamp.svg"},{"if":"theme=street_lighting_assen","then":"./assets/layers/street_lamps/street_lamp.svg"},{"if":"theme=surveillance","then":"./assets/themes/surveillance/logo.svg"},{"if":"theme=toerisme_vlaanderen","then":"./assets/svg/teardrop_with_hole_green.svg"},{"if":"theme=toilets","then":"./assets/themes/toilets/toilets.svg"},{"if":"theme=trees","then":"./assets/themes/trees/logo.svg"},{"if":"theme=uk_addresses","then":"./assets/themes/uk_addresses/housenumber_unknown.svg"},{"if":"theme=waste_basket","then":"./assets/themes/waste_basket/waste_basket.svg"}] | ||||
| 
 | ||||
|           }, | ||||
|           "iconSize": "30,30,bottom" | ||||
|         } | ||||
|       ], | ||||
|       "filter": [ | ||||
|         { | ||||
|           "id": "theme-search", | ||||
|           "options": [ | ||||
|             { | ||||
|               "osmTags": "theme~.*{search}.*", | ||||
|               "fields": [ | ||||
|                 { | ||||
|                   "name": "search" | ||||
|                 } | ||||
|               ], | ||||
|               "question": { | ||||
|                 "en": "Themename contains {search}" | ||||
|               } | ||||
|             } | ||||
|           ] | ||||
|         }, | ||||
|         { | ||||
|           "id": "created_by", | ||||
|           "options": [ | ||||
|             { | ||||
|               "osmTags": "_last_edit:contributor:lc~.*{search}.*", | ||||
|               "fields": [ | ||||
|                 { | ||||
|                   "name": "search" | ||||
|                 } | ||||
|               ], | ||||
|               "question": { | ||||
|                 "en": "Made by contributor {search}" | ||||
|               } | ||||
|             } | ||||
|           ] | ||||
|         }, | ||||
|         { | ||||
|           "id": "not_created_by", | ||||
|           "options": [ | ||||
|             { | ||||
|               "osmTags": "_last_edit:contributor:lc!~.*{search}.*", | ||||
|               "fields": [ | ||||
|                 { | ||||
|                   "name": "search" | ||||
|                 } | ||||
|               ], | ||||
|               "question": { | ||||
|                 "en": "<b>Not</b> made by contributor {search}" | ||||
|               } | ||||
|             } | ||||
|           ] | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "builtin": "current_view", | ||||
|       "override": { | ||||
|         "title": "Statistics on changesets in the current view", | ||||
|         "tagRenderings": [ | ||||
|           { | ||||
|             "id": "link_to_more", | ||||
|             "render": { | ||||
|               "en": "More statistics can be found <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>here</a>" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "id": "hist_themes", | ||||
|             "render": "{histogram(_embedded_cs:themes, Themename, Number of changesets)}" | ||||
|           }, | ||||
|           { | ||||
|             "id": "hist_themes", | ||||
|             "render": "{histogram(_embedded_cs:users, Contributor, Number of changesets)}" | ||||
|           } | ||||
|         ], | ||||
|         "calculatedTags": [ | ||||
|           "_embedded_cs=feat.overlapWith('mapcomplete-changes').map(f => f.feat.properties)", | ||||
|           "_embedded_cs:themes=feat.get('_embedded_cs').map(cs => cs.theme)", | ||||
|           "_embedded_cs:users=feat.get('_embedded_cs').map(cs => cs['_last_edit:contributor'])" | ||||
|         ], | ||||
|         "+mapRendering": [{ | ||||
|           "location": [ | ||||
|             "point" | ||||
|           ], | ||||
|           "icon":"statistics:black", | ||||
|           "iconSize": "30,30,center" | ||||
|         }] | ||||
|          | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
| } | ||||
|  | @ -748,14 +748,14 @@ video { | |||
|   right: 0.75rem; | ||||
| } | ||||
| 
 | ||||
| .top-4 { | ||||
|   top: 1rem; | ||||
| } | ||||
| 
 | ||||
| .bottom-0 { | ||||
|   bottom: 0px; | ||||
| } | ||||
| 
 | ||||
| .top-4 { | ||||
|   top: 1rem; | ||||
| } | ||||
| 
 | ||||
| .right-1\/3 { | ||||
|   right: 33.333333%; | ||||
| } | ||||
|  | @ -1096,6 +1096,10 @@ video { | |||
|   width: 6rem; | ||||
| } | ||||
| 
 | ||||
| .w-6 { | ||||
|   width: 1.5rem; | ||||
| } | ||||
| 
 | ||||
| .w-10 { | ||||
|   width: 2.5rem; | ||||
| } | ||||
|  | @ -1120,8 +1124,8 @@ video { | |||
|   width: 2.75rem; | ||||
| } | ||||
| 
 | ||||
| .w-6 { | ||||
|   width: 1.5rem; | ||||
| .w-4 { | ||||
|   width: 1rem; | ||||
| } | ||||
| 
 | ||||
| .w-16 { | ||||
|  | @ -1182,23 +1186,6 @@ video { | |||
|   transform: var(--tw-transform); | ||||
| } | ||||
| 
 | ||||
| @-webkit-keyframes spin { | ||||
|   to { | ||||
|     transform: rotate(360deg); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @keyframes spin { | ||||
|   to { | ||||
|     transform: rotate(360deg); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .animate-spin { | ||||
|   -webkit-animation: spin 1s linear infinite; | ||||
|           animation: spin 1s linear infinite; | ||||
| } | ||||
| 
 | ||||
| @-webkit-keyframes pulse { | ||||
|   50% { | ||||
|     opacity: .5; | ||||
|  | @ -1216,6 +1203,23 @@ video { | |||
|           animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; | ||||
| } | ||||
| 
 | ||||
| @-webkit-keyframes spin { | ||||
|   to { | ||||
|     transform: rotate(360deg); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @keyframes spin { | ||||
|   to { | ||||
|     transform: rotate(360deg); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .animate-spin { | ||||
|   -webkit-animation: spin 1s linear infinite; | ||||
|           animation: spin 1s linear infinite; | ||||
| } | ||||
| 
 | ||||
| .cursor-pointer { | ||||
|   cursor: pointer; | ||||
| } | ||||
|  | @ -1339,6 +1343,10 @@ video { | |||
|   border-radius: 0.5rem; | ||||
| } | ||||
| 
 | ||||
| .rounded-sm { | ||||
|   border-radius: 0.125rem; | ||||
| } | ||||
| 
 | ||||
| .rounded-l { | ||||
|   border-top-left-radius: 0.25rem; | ||||
|   border-bottom-left-radius: 0.25rem; | ||||
|  | @ -1564,16 +1572,16 @@ video { | |||
|   line-height: 1.75rem; | ||||
| } | ||||
| 
 | ||||
| .text-2xl { | ||||
|   font-size: 1.5rem; | ||||
|   line-height: 2rem; | ||||
| } | ||||
| 
 | ||||
| .text-lg { | ||||
|   font-size: 1.125rem; | ||||
|   line-height: 1.75rem; | ||||
| } | ||||
| 
 | ||||
| .text-2xl { | ||||
|   font-size: 1.5rem; | ||||
|   line-height: 2rem; | ||||
| } | ||||
| 
 | ||||
| .text-4xl { | ||||
|   font-size: 2.25rem; | ||||
|   line-height: 2.5rem; | ||||
|  |  | |||
|  | @ -112,12 +112,13 @@ class LayerOverviewUtils { | |||
| 
 | ||||
|         writeFileSync("./assets/generated/known_layers.json", JSON.stringify(Array.from(sharedLayers.values()))) | ||||
| 
 | ||||
|         /* | ||||
|         writeFileSync('./assets/themes/mapcomplete-changes/icons-mapping.txt', JSON.stringify( | ||||
|             Array.from(sharedThemes.values()).map(th => ({ | ||||
|                 if: "theme=" + th.id, | ||||
|                 then: th.icon | ||||
|             })) | ||||
|         )) | ||||
|         )) //*/
 | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue