Modify

Opened 3 years ago

Last modified 13 months ago

#21286 reopened enhancement

[Patch] Add validate rule min_height < height

Reported by: hubaishan Owned by: team
Priority: normal Milestone: 23.05
Component: Core validator Version:
Keywords: height min_height building:levels building:min_level Cc:

Description

If element has min_height tag so it must have height tag and height > min_height

Attachments (5)

josm_21286.patch (1.6 KB ) - added by skyper 19 months ago.
patch
josm_21286_v2.patch (1.3 KB ) - added by skyper 19 months ago.
patch version 2: regex instead of multiple lines
josm_21286_v3.patch (1.6 KB ) - added by skyper 19 months ago.
patch version 3: use complete strings instead of substrings in regex plus only compare values without unit plus " m" or " ft"
josm_21286_v4.patch (4.2 KB ) - added by skyper 19 months ago.
patch version 4: adds numeric test for min_height
21286.patch (16.5 KB ) - added by taylor.smock 19 months ago.
Use ' instead of ft

Download all attachments as: .zip

Change History (44)

comment:1 by skyper, 3 years ago

There is a collision in the use of min_height=* regarding attractions like roller-coasters, see OSM-wiki. Barriers could have a min_height=* without height=*, too. Do we get together some white or black list regarding primary tags?

Last edited 3 years ago by skyper (previous) (diff)

comment:2 by hubaishan, 3 years ago

So the rule may use building=* or building:part=* as condition

comment:3 by ivanbranco, 19 months ago

Hi, I posted a issue on the Osmose GitHub and they redirect me here: https://github.com/osm-fr/osmose-backend/issues/1842

In addition to this check I would like to propose to flag building:levels<building:min_levels as well.

comment:4 by skyper, 19 months ago

Keywords: height min_height building:levels building:min_level added
Owner: changed from team to skyper
Status: newassigned

One question: Are equal values for building:levels and building:min_level ok or not?

by skyper, 19 months ago

Attachment: josm_21286.patch added

patch

comment:5 by skyper, 19 months ago

Please, find attached patch which adds warnings for min_height without height resp. building:min_level without building:levels and in case the min_* tag's value is not lower. All only for areas and building=* plus building:part=*, so far.

I did not test it on real data.

comment:6 by skyper, 19 months ago

Milestone: 23.05
Owner: changed from skyper to team
Status: assignednew

comment:7 by skyper, 19 months ago

Summary: Add validate rule min_height < height[Patch] Add validate rule min_height < height

in reply to:  4 ; comment:8 by taylor.smock, 19 months ago

Replying to skyper:

One question: Are equal values for building:levels and building:min_level ok or not?

It looks like not. See https://wiki.openstreetmap.org/w/images/3/3a/Min_level_2.svg

In other words, if building:levels === building:min_level, then there is no actual building part there. Maybe just a building:part=roof?

I assume that we are only looking for building and building:part, not not:building or not:building:part. If so, why isn't the regex /^(building|building:part)$/?

by skyper, 19 months ago

Attachment: josm_21286_v2.patch added

patch version 2: regex instead of multiple lines

in reply to:  8 ; comment:9 by skyper, 19 months ago

Replying to taylor.smock:

I assume that we are only looking for building and building:part, not not:building or not:building:part. If so, why isn't the regex /^(building|building:part)$/?

I was not aware that "positive" regex work with placeholders. New patch is two lines shorter now.

in reply to:  9 ; comment:10 by taylor.smock, 19 months ago

Replying to skyper:

I was not aware that "positive" regex work with placeholders. New patch is two lines shorter now.

That isn't exactly what I meant, but it does seem to work. Here is what I was thinking:

- area[min_height                ][!height][/(building|building:part)/], /* #21286 */
+ area[min_height                ][!height][/^(building|building:part)$/], /* #21286 */

The advantage of ^(building|building:part)$ is we don't have to check not:building (and it won't match building:levels). The regexp parser will check the n and go "nope, the first character is not b, this does not match". We might be able to "optimize" the regex a bit, by using ^building(|:part)$, but I don't think that is necessary.
One caveat for the tag(height) <= tag(min_height): this will only work for where the user has mapped the height and min_height using metric units.

in reply to:  10 ; comment:11 by skyper, 19 months ago

Replying to taylor.smock:

Replying to skyper:

I was not aware that "positive" regex work with placeholders. New patch is two lines shorter now.

That isn't exactly what I meant, but it does seem to work. Here is what I was thinking:

- area[min_height                ][!height][/(building|building:part)/], /* #21286 */
+ area[min_height                ][!height][/^(building|building:part)$/], /* #21286 */

The advantage of ^(building|building:part)$ is we don't have to check not:building (and it won't match building:levels). The regexp parser will check the n and go "nope, the first character is not b, this does not match". We might be able to "optimize" the regex a bit, by using ^building(|:part)$, but I don't think that is necessary.

Oh, I see. Now, I better understand why I had problems with regex in the past as I never realized that it describes only substrings. Thought I need [/.*(building|building:part).*/] to work like that.

One caveat for the tag(height) <= tag(min_height): this will only work for where the user has mapped the height and min_height using metric units.

Good spot. Guess I can only compare metric units or even only values in meters. Else I would need to calculate comparable values first which seems to be quite more work. I tried following but it does not work and assertMatch is no help:

area[/^(building|building:part)$/][height =~ /^[0-9]+(\.[0-9]+)?(( )(metre|metres|meter|meters|Metre|Metres|Meter|Meters)|m)?$/][min_height =~ /^[0-9]+(\.[0-9]+)?(( )(metre|metres|meter|meters|Metre|Metres|Meter|Meters)|m)?$/][get(split(" ", tag(height)), 0) <= get(split(" ", tag(min_height)), 0)],
area[/^(building|building:part)$/][building:levels][building:min_level][tag("building:levels") <= tag("building:min_level")] {
  throwWarning: tr("{0} is lower or equal to {1} on {2}", "{1.key}", "{2.key}", "{0.key}");
  group: tr("suspicious tag combination");
  assertMatch:   "area buiding=yes height=10 min_height=\"10 m\"";
  assertNoMatch: "area buiding=yes height=10 min_height=\"9 m\"";
}

in reply to:  11 comment:12 by taylor.smock, 19 months ago

Replying to skyper:

Oh, I see. Now, I better understand why I had problems with regex in the past as I never realized that it describes only substrings. Thought I need [/.*(building|building:part).*/] to work like that.

There are various regex pitfalls. You can use https://regex101.com/ (select the Java 8 flavor) to test regexes in the future. Big thing: ^ matches the start of a string and $ matches the end of the line, so it is constraining the regex to match either building or building:part and nothing else.

Good spot. Guess I can only compare metric units or even only values in meters. Else I would need to calculate comparable values first which seems to be quite more work. I tried following but it does not work and assertMatch is no help:

There was a ticket about adding support for converting units, but stoecker killed it IIRC. I cannot find that ticket anymore though. :(

EDIT: To clarify, I don't know what we can do besides only work with meters, right now anyway. I would note that I would follow the wiki specification for units. So meters would just be m, feet would be ", yards would be '. So the regex would look like ^[0-9]+(\.[0-9]+)?( m)?$.

Last edited 19 months ago by taylor.smock (previous) (diff)

by skyper, 19 months ago

Attachment: josm_21286_v3.patch added

patch version 3: use complete strings instead of substrings in regex plus only compare values without unit plus " m" or " ft"

comment:13 by skyper, 19 months ago

Thanks, got it working, see josm_21286_v3.patch

comment:14 by skyper, 19 months ago

I took the chance and have added the common numerical test for min_height in josm_21286_v4.patch

by skyper, 19 months ago

Attachment: josm_21286_v4.patch added

patch version 4: adds numeric test for min_height

comment:15 by taylor.smock, 19 months ago

We should be following the osmwiki:Map_features/Units specifications. I'll upload your patch, modified, to show what I think it should be.

comment:16 by taylor.smock, 19 months ago

It looks like we are adding ft in some locations. I'm surprised we haven't heard any complaints from users, since we aren't following the documented specification.

by taylor.smock, 19 months ago

Attachment: 21286.patch added

Use ' instead of ft

comment:17 by Famlam, 19 months ago

@taylor.smock

It looks like we are adding ft in some locations. I'm surprised we haven't heard any complaints from users, since we aren't following the documented specification.
Use ' instead of ft

Regarding the changed feet/inches tests:
The warning:
tr("unusual value of {0}: use abbreviation for unit and space between value and unit", "{0.key}");
is very odd to see for
maxheight=22 ' (note the space between 22 and ', which is actually undesired).

Maybe the message for the feet/inch-tests should be revised? (It's slightly offtopic for this issue, so I can also file a followup report if you prefer)

in reply to:  17 comment:18 by taylor.smock, 19 months ago

Replying to Famlam:

Maybe the message for the feet/inch-tests should be revised? (It's slightly offtopic for this issue, so I can also file a followup report if you prefer)

Probably. I don't know what a good, clear, message would be. I don't think unusual value of {0}: use appropriate unit syntax" will cut it.

comment:19 by Famlam, 19 months ago

How about:
tr("unusual value of {0}: use single/double quote symbols for the imperial unit and no space between value and unit", "{0.key}");
?

Last edited 19 months ago by Famlam (previous) (diff)

comment:20 by skyper, 19 months ago

Damn, I just thought it would be smart to add the value test for min_height but instead stepped into a honey pot. Now, we should add a rule with autofix for space between value and the inch symbol " and the use of two single quotes instead of a double quote.
@Famlam's proposed message looks fine to me but how to generalize the message of the last rule tr("unusual value of {0}: {1} is default; point is decimal separator; if units, put space then unit", "{0.key}", tr("meters"))

comment:21 by Famlam, 18 months ago

Now, we should add a rule with autofix for space between value and the inch symbol " and the use of two single quotes instead of a double quote.

Well, the issue itself has been addressed with the current patch. Making the inch-rules perfect could be a separate issue?

@Famlam's proposed message looks fine to me but how to generalize the message of the last rule tr("unusual value of {0}: {1} is default; point is decimal separator; if units, put space then unit", "{0.key}", tr("meters"))

tr("unusual value of {0}: {1} is default; point is decimal separator; if units, use a space before the unit or use single/double quote symbols for imperial units", "{0.key}", tr("meters"))

Bit long, but the other option would probably to split it in two rules.
Or we should ignore imperial in the message for this rule, there's only about 30 min_height values using imperial A'B" worldwide ;)

comment:22 by taylor.smock, 18 months ago

I'll go ahead and apply a patch that only deals with metric units.

comment:23 by taylor.smock, 18 months ago

Resolution: fixed
Status: newclosed

In 18731/josm:

Fix #21286: Add validator rule for min_height < height (patch by skyper, modified)

This additionally fixes an error introduced in r18730.

comment:24 by stoecker, 18 months ago

Why the tr("meters")? That makes no sense and harder to translate.

in reply to:  24 comment:25 by skyper, 18 months ago

Replying to taylor.smock:

I'll go ahead and apply a patch that only deals with metric units.

Now, we get strange results as we only warn about ft with min_height and e.g. not with height and the message for the general warning without fix is still "unusual value of {0}: {1} is default; point is decimal separator; if units, put space then unit", "{0.key}", tr("meters"). We do not fix the comma as decimal separator with ft, as-well.

Replying to stoecker:

Why the tr("meters")? That makes no sense and harder to translate.

It is a copy of the warning for height. I get your point, thought, there are similar uses of placeholder for units in the file. Looks like the file needs some rework besides the problems with the correct unit symbol for foot.

comment:26 by taylor.smock, 18 months ago

We really need to update the numeric.mapcss file to not recommend the aliases for units (of which ft is one).

What we should probably do is the following:

  1. Have alias replacement checks first (with autofixes, so /^(?i)(metres?|meters?)$/ would become m, and so on).
  2. Then do the , to . replacements (so /^[0-9]*,[0-9]*( m)?$/).
  3. Convert to the default unit (or another arbitrary unit, if there is no default, like with power).
    *[height =~ /^[0-9]*\.[0-9]*( m)?$/] {
        height: get(split(" ", tag("height")), 0);
    }
    *[height =~/^([1-9][0-9]*\'((10|11|[0-9])((\.[0-9]+)?)\")?)$/] {
        // Might be able to reuse regex above via {0.value}
        _tmp: regexp_match("^([1-9][0-9]*\'((10|11|[0-9])((\.[0-9]+)?)\")?)$", tag("height"));
        height: eval((get(prop(_tmp), 1) + 12 * get(prop(_tmp), 2)) * 0.0254);
    }
    
  4. Start doing the actual comparisons (like checking for min_height < height using prop and is_prop_set).

Why the tr("meters")?

Do you have a better suggestion? We don't translate meter anywhere in source, and it was (as skyper mentioned) used in a previous warning message.

comment:27 by stoecker, 18 months ago

In a text like this simply move it inline (replace {1} with meters).

I know that programmers tend to "optimize" such situations, but for i18n sometimes duplication with less complexity is better.

comment:28 by taylor.smock, 18 months ago

That is totally fair.

I just finished going through the numeric.mapcss unit checks, and realized that fixAdd: concat("{0.key}", "=", get(regexp_match("([0-9.]+) *.+", "{0.key}"), 1), " m"); doesn't work. :(

It looks like it is unsupported, see wikipedia.mapcss:

  /* fixAdd: concat("{0.key}", "=", get(regexp_match("(?i)^([-a-z]+:)(.*)$", tag("{0.key}")),1), trim(replace(URL_decode(get(regexp_match("(?i)^([-a-z]+:)(.+)$", tag("{0.key}")),2)), "_", " "))); */
    /* tag("{0.key}") is not yet supported */

In order to fix this, there are a few interfaces that need to change (mostly to pass a Selector through -- we need the information to get the right key/value/tag).

Do we know if there are any users of our mapcss implementation?

comment:29 by Famlam, 18 months ago

Correct, the {N.key/tag/value} blocks are unfortunately only replaced by the key/value/tag after all the functions have already been called. I recently gave the following example @Osmose:

the hypothetical fixAdd: concat("x=", replace("{0.value}", "value", "key")); on way a=b would give x=a as output, rather than x=b.

I'd love to see this sequence being changed, so you can actually do things with the functions on the keys/values/tags without hardcoding them.

comment:30 by taylor.smock, 18 months ago

I was just looking at that. I think I can just add it as a field to the Environment and check and see if it exists. I've already got a special case record for anything with a {i.tag|key|value}, and I just need to figure out where the environment variable is being set for the various code paths.

So it might change soon. I'll have to open a separate ticket for it though. Unless I find a ticket that someone else already opened.

comment:31 by Famlam, 18 months ago

Unless I find a ticket that someone else already opened.

You can even choose out of two, although they seem to also want to change the output of {0.value} (etc) to no longer provide the regex (for a regex match) but the actual value. That's another step on top of this.
#17669, #22096
Not dedicated for regexp_match nor replace, but I guess it's one and the same issue

Last edited 18 months ago by Famlam (previous) (diff)

comment:32 by taylor.smock, 18 months ago

I'm going to go ahead and use #17669.

I'm attaching the changes I made to numeric.mapcss to that patch, since those changes requires that the placeholder replacements work.

Unless I find a ticket that someone else already opened.

Well, I guess I already opened a separate ticket for this. I just forgot about it...

comment:33 by osm@…, 17 months ago

I'm coming here, because Osmose is now enforcing your check that building:min_level must be < building:levels.
That check is wrong, because building:levels only counts non-roof:levels, so a roof-only part of a building has building:min_level == building:levels and if you want to skip the first roof-floor, it's even higher.
The height-checks are fine, because building:height is the total height, including the roof:height, but building:levels does not include roof:levels.

comment:34 by Nadjita, 17 months ago

Resolution: fixed
Status: closedreopened

comment:35 by Nadjita, 17 months ago

If you really want a check, then building:min_level < building:levels+roof:levels

Again, building:levels does not contain roof:levels, so you cannot compare it to height, which includes roof:height.

comment:36 by Metzor, 16 months ago

I agree with Nadjita.

Without considering roof:levels, there is, for instance, a lot of false-positives for 3D roof modeling. Simple3D documentation advises to avoid overlapping faces as much as possible. For (intentionaly) overlapping roof:shape overlap of lower levels can be avoided by setting building:min_level to building:levels. That's one of the readons why I'd appreciate, if someone implements Nadjita's suggestion.

Fun fact: There is buildings with zero building:levels.

comment:37 by skyper, 13 months ago

Would excluding building=roof and building:part=roof work? Then roofs could be treated in another rule.
I would really appreciate if someone could document the special cases for roofs on the OSM wiki pages.

comment:38 by Nadjita, 13 months ago

If you are describing a building part, then you use the function. So an office at roof-level is building:part=office, no matter if it's a roof or not. You only use roof, if you don't have anything else to put in there, much like building:part=yes. Can we please just get rid of this nonsensical check? Screwing things up and then wanting to change the wiki to make it right seems like a bad idea…

comment:39 by gaben, 13 months ago

Could you please add also roof:levels to the ignoretags?

Modify Ticket

Change Properties
Set your email in Preferences
Action
as reopened The owner will remain team.
as The resolution will be set. Next status will be 'closed'.
to The owner will be changed from team to the specified user. Next status will be 'new'.
Next status will be 'needinfo'. The owner will be changed from team to hubaishan.
as duplicate The resolution will be set to duplicate. Next status will be 'closed'. The specified ticket will be cross-referenced with this ticket.

Add Comment


E-mail address and name can be saved in the Preferences .
 
Note: See TracTickets for help on using tickets.