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,10 +58,10 @@ 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
|
||||||
|
|
||||||
// Datalayer displaying the road and the cut points (if any)
|
// Datalayer displaying the road and the cut points (if any)
|
||||||
new ShowDataLayer({
|
new ShowDataLayer({
|
||||||
features: new StaticFeatureSource(splitPoints, true),
|
features: new StaticFeatureSource(splitPoints, true),
|
||||||
|
|
|
@ -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
|
@ -13,19 +13,19 @@ export default class T {
|
||||||
* Returns an empty list if successful
|
* Returns an empty list if successful
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
public Run() : ({testsuite: string, name: string, msg: string} []) {
|
public Run(): ({ testsuite: string, name: string, msg: string } []) {
|
||||||
const failures: {testsuite: string, name: string, msg: string} [] = []
|
const failures: { testsuite: string, name: string, msg: string } [] = []
|
||||||
for (const [name, test] of this._tests) {
|
for (const [name, test] of this._tests) {
|
||||||
try {
|
try {
|
||||||
test();
|
test();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
failures.push({testsuite: this.name, name: name, msg: ""+e});
|
failures.push({testsuite: this.name, name: name, msg: "" + e});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (failures.length == 0) {
|
if (failures.length == 0) {
|
||||||
return undefined
|
return undefined
|
||||||
} else {
|
} else {
|
||||||
return failures
|
return failures
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,12 +40,12 @@ export default class T {
|
||||||
throw "Expected true, but got false: " + msg
|
throw "Expected true, but got false: " + msg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static equals(a, b, msg?){
|
static equals(a, b, msg?) {
|
||||||
if(a !== b){
|
if (a !== b) {
|
||||||
throw "Not the same: "+(msg??"")+"\n" +
|
throw "Not the same: " + (msg ?? "") + "\n" +
|
||||||
"Expcected: "+a+"\n" +
|
"Expcected: " + a + "\n" +
|
||||||
"Got : "+b
|
"Got : " + b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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