forked from MapComplete/MapComplete
		
	Fix split way action, add decent tests for them (fix #171), enable split road on cyclestreets theme
This commit is contained in:
		
							parent
							
								
									affe8237dc
								
							
						
					
					
						commit
						a2aa26aafc
					
				
					 12 changed files with 1908 additions and 60 deletions
				
			
		|  | @ -48,6 +48,7 @@ export default class DynamicGeoJsonTileSource extends DynamicTileSource { | ||||||
|                 if(whitelist !== undefined){ |                 if(whitelist !== undefined){ | ||||||
|                     const isWhiteListed = whitelist.get(zxy[1])?.has(zxy[2]) |                     const isWhiteListed = whitelist.get(zxy[1])?.has(zxy[2]) | ||||||
|                     if(!isWhiteListed){ |                     if(!isWhiteListed){ | ||||||
|  |                         console.log("Not downloading tile", ...zxy, "as it is not on the whitelist") | ||||||
|                         return undefined; |                         return undefined; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  | @ -109,6 +109,8 @@ export default class OsmFeatureSource { | ||||||
| 
 | 
 | ||||||
|                 geojson.features = geojson.features.filter(feature => this.allowedTags.matchesProperties(feature.properties)) |                 geojson.features = geojson.features.filter(feature => this.allowedTags.matchesProperties(feature.properties)) | ||||||
| 
 | 
 | ||||||
|  |                 geojson.features.forEach(f => f.properties["_backend"] = this._backend) | ||||||
|  |                  | ||||||
|                 console.log("Tile geojson:", z, x, y, "is", geojson) |                 console.log("Tile geojson:", z, x, y, "is", geojson) | ||||||
|                 const index = Tiles.tile_index(z, x, y); |                 const index = Tiles.tile_index(z, x, y); | ||||||
|                 new PerLayerFeatureSourceSplitter(this.filteredLayers, |                 new PerLayerFeatureSourceSplitter(this.filteredLayers, | ||||||
|  |  | ||||||
|  | @ -230,7 +230,7 @@ export class GeoOperations { | ||||||
|      *  The properties object will contain three values: |      *  The properties object will contain three values: | ||||||
|      // - `index`: closest point was found on nth line part,
 |      // - `index`: closest point was found on nth line part,
 | ||||||
|      // - `dist`: distance between pt and the closest point (in kilometer),
 |      // - `dist`: distance between pt and the closest point (in kilometer),
 | ||||||
|      // `location`: distance along the line between start and the closest point.
 |      // `location`: distance along the line between start (of the line) and the closest point.
 | ||||||
|      * @param way The road on which you want to find a point |      * @param way The road on which you want to find a point | ||||||
|      * @param point Point defined as [lon, lat] |      * @param point Point defined as [lon, lat] | ||||||
|      */ |      */ | ||||||
|  |  | ||||||
|  | @ -15,17 +15,21 @@ export default class SplitAction extends OsmChangeAction { | ||||||
|     private readonly wayId: string; |     private readonly wayId: string; | ||||||
|     private readonly _splitPointsCoordinates: [number, number] []// lon, lat
 |     private readonly _splitPointsCoordinates: [number, number] []// lon, lat
 | ||||||
|     private _meta: { theme: string, changeType: "split" }; |     private _meta: { theme: string, changeType: "split" }; | ||||||
|  |     private _toleranceInMeters: number; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      *  |      * Create a changedescription for splitting a point. | ||||||
|  |      * Will attempt to reuse existing points | ||||||
|      * @param wayId |      * @param wayId | ||||||
|      * @param splitPointCoordinates: lon, lat |      * @param splitPointCoordinates: lon, lat | ||||||
|      * @param meta |      * @param meta | ||||||
|  |      * @param toleranceInMeters: if a splitpoint closer then this amount of meters to an existing point, the existing point will be used to split the line instead of a new point | ||||||
|      */ |      */ | ||||||
|     constructor(wayId: string, splitPointCoordinates: [number, number][], meta: {theme: string}) { |     constructor(wayId: string, splitPointCoordinates: [number, number][], meta: {theme: string},  toleranceInMeters = 5) { | ||||||
|         super() |         super() | ||||||
|         this.wayId = wayId; |         this.wayId = wayId; | ||||||
|         this._splitPointsCoordinates = splitPointCoordinates |         this._splitPointsCoordinates = splitPointCoordinates | ||||||
|  |         this._toleranceInMeters = toleranceInMeters; | ||||||
|         this._meta = {...meta, changeType: "split"}; |         this._meta = {...meta, changeType: "split"}; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -51,7 +55,7 @@ export default class SplitAction extends OsmChangeAction { | ||||||
|         const originalNodes = originalElement.nodes; |         const originalNodes = originalElement.nodes; | ||||||
| 
 | 
 | ||||||
|         // First, calculate splitpoints and remove points close to one another
 |         // First, calculate splitpoints and remove points close to one another
 | ||||||
|         const splitInfo = this.CalculateSplitCoordinates(originalElement) |         const splitInfo = this.CalculateSplitCoordinates(originalElement, this._toleranceInMeters) | ||||||
|         // Now we have a list with e.g. 
 |         // Now we have a list with e.g. 
 | ||||||
|         // [ { originalIndex: 0}, {originalIndex: 1, doSplit: true}, {originalIndex: 2}, {originalIndex: undefined, doSplit: true}, {originalIndex: 3}]
 |         // [ { originalIndex: 0}, {originalIndex: 1, doSplit: true}, {originalIndex: 2}, {originalIndex: undefined, doSplit: true}, {originalIndex: 3}]
 | ||||||
| 
 | 
 | ||||||
|  | @ -230,17 +234,19 @@ export default class SplitAction extends OsmChangeAction { | ||||||
|                 // We keep the original points
 |                 // We keep the original points
 | ||||||
|                 continue |                 continue | ||||||
|             } |             } | ||||||
|             if (point.dist * 1000 >= toleranceInM) { |  | ||||||
|                 // No need to remove this one
 |  | ||||||
|                 continue |  | ||||||
|             } |  | ||||||
|           |           | ||||||
|             // At this point, 'dist' told us the point is pretty close to an already existing point.
 |             // At this point, 'dist' told us the point is pretty close to an already existing point.
 | ||||||
|             // Lets see which (already existing) point is closer and mark it as splitpoint
 |             // Lets see which (already existing) point is closer and mark it as splitpoint
 | ||||||
|             const nextPoint = allPoints[i + 1] |             const nextPoint = allPoints[i + 1] | ||||||
|             const prevPoint = allPoints[i - 1] |             const prevPoint = allPoints[i - 1] | ||||||
|             const distToNext = nextPoint.location - point.location |             const distToNext = nextPoint.location - point.location | ||||||
|             const distToPrev = prevPoint.location - point.location |             const distToPrev = point.location - prevPoint.location | ||||||
|  |              | ||||||
|  |             if(distToNext * 1000 > toleranceInM && distToPrev * 1000 > toleranceInM){ | ||||||
|  |                 // Both are too far away to mark them as the split point
 | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |              | ||||||
|             let closest = nextPoint |             let closest = nextPoint | ||||||
|             if (distToNext > distToPrev) { |             if (distToNext > distToPrev) { | ||||||
|                 closest = prevPoint |                 closest = prevPoint | ||||||
|  |  | ||||||
|  | @ -25,7 +25,8 @@ export default class SimpleMetaTagger { | ||||||
|                 "_last_edit:contributor:uid", |                 "_last_edit:contributor:uid", | ||||||
|                 "_last_edit:changeset", |                 "_last_edit:changeset", | ||||||
|                 "_last_edit:timestamp", |                 "_last_edit:timestamp", | ||||||
|                 "_version_number"], |                 "_version_number", | ||||||
|  |             "_backend"], | ||||||
|             doc: "Information about the last edit of this object." |             doc: "Information about the last edit of this object." | ||||||
|         }, |         }, | ||||||
|         (feature) => {/*Note: also called by 'UpdateTagsFromOsmAPI'*/ |         (feature) => {/*Note: also called by 'UpdateTagsFromOsmAPI'*/ | ||||||
|  |  | ||||||
|  | @ -90,14 +90,8 @@ export default class LayoutConfig { | ||||||
|         this.startZoom = json.startZoom; |         this.startZoom = json.startZoom; | ||||||
|         this.startLat = json.startLat; |         this.startLat = json.startLat; | ||||||
|         this.startLon = json.startLon; |         this.startLon = json.startLon; | ||||||
|         if(json.widenFactor < 0.02){ |         if(json.widenFactor <= 0){ | ||||||
|             if(official){ |                 throw "Widenfactor too small, shoud be > 0" | ||||||
|                 throw "Widenfactor too small" |  | ||||||
|             }else{ |  | ||||||
|                 // Unofficial themes get away with this
 |  | ||||||
|                 console.warn("Detected a very small widenfactor for theme ", this.id ,", bumping this above 1.") |  | ||||||
|                 json.widenFactor = json.widenFactor + 1 |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|         if(json.widenFactor > 20){ |         if(json.widenFactor > 20){ | ||||||
|             throw "Widenfactor is very big, use a value between 1 and 5 (current value is "+json.widenFactor+") at "+context |             throw "Widenfactor is very big, use a value between 1 and 5 (current value is "+json.widenFactor+") at "+context | ||||||
|  |  | ||||||
|  | @ -52,23 +52,19 @@ export default class MoreScreen extends Combine { | ||||||
| 
 | 
 | ||||||
|     private static createUnofficialThemeList(buttonClass: string, state: UserRelatedState, themeListClasses): BaseUIElement { |     private static createUnofficialThemeList(buttonClass: string, state: UserRelatedState, themeListClasses): BaseUIElement { | ||||||
|         return new VariableUiElement(state.installedThemes.map(customThemes => { |         return new VariableUiElement(state.installedThemes.map(customThemes => { | ||||||
|             const els: BaseUIElement[] = [] |             if (customThemes.length <= 0) { | ||||||
|             if (customThemes.length > 0) { |                 return undefined; | ||||||
|                 const customThemesElement = new Combine( |  | ||||||
|                     customThemes.map(theme => MoreScreen.createLinkButton(state, theme.layout, theme.definition)?.SetClass(buttonClass)) |  | ||||||
|                 ) |  | ||||||
|                 els.push(customThemesElement) |  | ||||||
|             } |             } | ||||||
|  |             const customThemeButtons = customThemes.map(theme => MoreScreen.createLinkButton(state, theme.layout, theme.definition)?.SetClass(buttonClass)) | ||||||
|             return new Combine([ |             return new Combine([ | ||||||
|                 Translations.t.general.customThemeIntro.Clone(), |                 Translations.t.general.customThemeIntro.Clone(), | ||||||
|                 new Combine(els).SetClass(themeListClasses) |                 new Combine(customThemeButtons).SetClass(themeListClasses) | ||||||
|             ]); |             ]); | ||||||
|         })); |         })); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private static createPreviouslyVistedHiddenList(state: UserRelatedState, buttonClass: string, themeListStyle: string) { |     private static createPreviouslyVistedHiddenList(state: UserRelatedState, buttonClass: string, themeListStyle: string) { | ||||||
|         const t = Translations.t.general.morescreen |         const t = Translations.t.general.morescreen | ||||||
|         console.log("Hidden themes init...") |  | ||||||
|         const prefix = "mapcomplete-hidden-theme-" |         const prefix = "mapcomplete-hidden-theme-" | ||||||
|         const hiddenTotal = AllKnownLayouts.layoutsList.filter(layout => layout.hideFromOverview).length |         const hiddenTotal = AllKnownLayouts.layoutsList.filter(layout => layout.hideFromOverview).length | ||||||
|         return new Toggle( |         return new Toggle( | ||||||
|  |  | ||||||
|  | @ -58,7 +58,7 @@ export default class SplitRoadWizard extends Toggle { | ||||||
|         miniMap.SetStyle("width: 100%; height: 24rem") |         miniMap.SetStyle("width: 100%; height: 24rem") | ||||||
|             .SetClass("rounded-xl overflow-hidden"); |             .SetClass("rounded-xl overflow-hidden"); | ||||||
| 
 | 
 | ||||||
|         miniMap.installBounds(BBox.get(roadElement)) |         miniMap.installBounds(BBox.get(roadElement).pad(0.25), false) | ||||||
| 
 | 
 | ||||||
|         // Define how a cut is displayed on the map
 |         // Define how a cut is displayed on the map
 | ||||||
|          |          | ||||||
|  |  | ||||||
|  | @ -284,6 +284,6 @@ | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
|   "overrideAll": { |   "overrideAll": { | ||||||
|     "allowSplit": false |     "allowSplit": true | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | @ -256,7 +256,7 @@ | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
|   "clustering": { |   "clustering": { | ||||||
|     "maxZoom": 16, |     "maxZoom": 6, | ||||||
|     "minNeededElements": 100 |     "minNeededElements": 100 | ||||||
|   }, |   }, | ||||||
|   "overrideAll": { |   "overrideAll": { | ||||||
|  |  | ||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -54,4 +54,21 @@ export default class T { | ||||||
|             throw "Expected false, but got true: " + msg |             throw "Expected false, but got true: " + msg | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     static listIdentical<T>(expected: T[], actual: T[]): void { | ||||||
|  |         if(expected === undefined){ | ||||||
|  |             throw "ListIdentical failed: expected list is undefined" | ||||||
|  |         } | ||||||
|  |         if(actual === undefined){ | ||||||
|  |             throw "ListIdentical failed: actual list is undefined" | ||||||
|  |         } | ||||||
|  |         if (expected.length !== actual.length) { | ||||||
|  |             throw `ListIdentical failed: expected a list of length ${expected.length} but got a list of length ${actual.length}` | ||||||
|  |         } | ||||||
|  |         for (let i = 0; i < expected.length; i++) { | ||||||
|  |             if (expected[i] !== actual[i]) { | ||||||
|  |             throw `ListIdentical failed at index ${i}: expected ${expected[i]} but got ${actual[i]}` | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue