Skip to content

Commit f62095e

Browse files
fix(map): fade non-matching toilets under WC sub-filters instead of hiding them (#3278)
* fix(map): fade non-matching toilets under WC sub-filters instead of hiding them The gender/accessibility sub-filters hid non-matching toilet markers outright via a setFilter on indoor-pois, while their room fills only faded (#3241). Drop the hide path and let applyFilterDim own the fade, so non-matching icons dim to 0.2 like the room fills and stay clickable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(map): treat male+female toilets as unisex; drop slop comments A toilet flagged both male and female is all-gender in practice, but the unisex sub-filter only matched the explicit `is_unisex_toilet` flag and so skipped them. The unisex selection now matches `is_unisex_toilet OR (is_male_toilet AND is_female_toilet)`, covering both the icon and room-fill layers. Also trims the verbose comment blocks added in this PR and removes the remaining test comment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(map): drop is_unisex_toilet, encode unisex as male AND female A unisex/all-gender toilet is usable by everyone, so there is no need for a flag of its own: it is now encoded as both is_male_toilet and is_female_toilet. This removes the is_unisex_toilet column from pois and rooms, and the unisex sub-filter, popup label, and POI sprite all derive the all-gender case from the male/female pair. - osm2pgsql: a `toilet_flags` helper folds `unisex=yes` into male+female once (also fixing the multipolygon-relation path that stored the raw tag string instead of a boolean). - migration: backfill existing explicitly-unisex rows into the male/female pair, then drop the column and re-emit the indoor_pois/indoor_rooms tiles without it. - basemap: a male+female toilet now gets the generic toilet sprite. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(map): fold is_unisex_toilet removal into its source migrations Rather than add-then-drop the column across two migrations, remove it at the source: the Jun-11 WC migrations are not yet in prod, and a full OSM re-import recreates pois/rooms from the (unisex-free) lua schema, so no DROP COLUMN or backfill is needed. - 20260611140000: indoor_pois no longer selects is_unisex_toilet. - 20260611160000: drops the rooms is_unisex_toilet column add and the indoor_rooms reference. - removes the standalone drop-migration added earlier. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(map): base martin image on :main so the docker build works ghcr.io/maplibre/martin:latest was reslimmed to a package-manager-less image, so `apt-get install unzip` fails (exit 127) and the build is red on main. The :main tag is Alpine and already ships wget + unzip, so drop the apt-get step and use busybox-safe flags (-q, -p). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent dd96be5 commit f62095e

9 files changed

Lines changed: 80 additions & 121 deletions

File tree

map/martin/Dockerfile

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
FROM ghcr.io/maplibre/martin:main
22

3-
RUN apt-get update && \
4-
apt-get install --no-install-recommends --yes unzip && \
5-
rm -rf /var/lib/apt/lists/*
6-
73
# -- fonts --
8-
RUN wget --quiet -O /tmp/roboto-android.zip https://github.com/googlefonts/roboto/releases/download/v2.138/roboto-android.zip && \
4+
RUN wget -q -O /tmp/roboto-android.zip https://github.com/googlefonts/roboto/releases/download/v2.138/roboto-android.zip && \
95
unzip -q /tmp/roboto-android.zip -d "$(dirname "$0")/fonts/" && \
106
rm /tmp/roboto-android.zip
117

128
# -- sprites --
13-
RUN wget --quiet -O /tmp/sprites_maki.zip https://github.com/mapbox/maki/zipball/main && \
14-
mkdir --parents "$(dirname "$0")/sprites/maki/" && \
9+
RUN wget -q -O /tmp/sprites_maki.zip https://github.com/mapbox/maki/zipball/main && \
10+
mkdir -p "$(dirname "$0")/sprites/maki/" && \
1511
unzip -q /tmp/sprites_maki.zip -d /tmp/sprites && \
1612
rm /tmp/sprites_maki.zip && \
1713
mv /tmp/sprites/mapbox-maki-*/icons/* "$(dirname "$0")/sprites/maki/" && \

map/martin/styles/navigatum-basemap.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5445,12 +5445,12 @@
54455445
"elevator"
54465446
],
54475447
"elevator",
5448+
["all", ["get", "is_male_toilet"], ["get", "is_female_toilet"]],
5449+
"toilet",
54485450
["get", "is_male_toilet"],
54495451
"toilet_male",
54505452
["get", "is_female_toilet"],
54515453
"toilet_female",
5452-
["get", "is_unisex_toilet"],
5453-
"toilet",
54545454
["==", ["get", "indoor"], "shower"],
54555455
"noun-shower-51",
54565456
""

map/osm2pgsql/style.lua

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ tables.rooms =
3939
-- non-matching toilet rooms the same way the icon filter hides their POIs.
4040
{ column = "is_male_toilet", type = "boolean", not_null = true },
4141
{ column = "is_female_toilet", type = "boolean", not_null = true },
42-
{ column = "is_unisex_toilet", type = "boolean", not_null = true },
4342
{ column = "is_wheelchair_toilet", type = "boolean", not_null = true },
4443
{ column = "level_min", type = "real", not_null = true },
4544
{ column = "level_max", type = "real", not_null = true },
@@ -62,7 +61,6 @@ tables.pois =
6261
{ column = "students_have_access", type = "boolean", not_null = true },
6362
{ column = "is_male_toilet", type = "boolean", not_null = true },
6463
{ column = "is_female_toilet", type = "boolean", not_null = true },
65-
{ column = "is_unisex_toilet", type = "boolean", not_null = true },
6664
{ column = "is_wheelchair_toilet", type = "boolean", not_null = true },
6765
{ column = "area", type = "real", not_null = true },
6866
{ column = "level_min", type = "real", not_null = true },
@@ -272,6 +270,19 @@ local function clean_tags_indoor(tags)
272270
return next(tags) == nil
273271
end
274272

273+
-- The typed toilet attribute booleans for a feature's tags. A unisex/all-gender toilet is usable
274+
-- by everyone; with no separate unisex flag it is encoded as both male and female. The flags only
275+
-- apply to `indoor == "toilet"`.
276+
local function toilet_flags(tags)
277+
local is_toilet = tags.indoor == "toilet"
278+
local unisex = tags.unisex == "yes"
279+
return {
280+
male = is_toilet and (tags.male == "yes" or unisex),
281+
female = is_toilet and (tags.female == "yes" or unisex),
282+
wheelchair = is_toilet and tags.wheelchair == "yes",
283+
}
284+
end
285+
275286
-- Called for every node in the input. The `object` argument contains all the
276287
-- attributes of the node like `id`, `version`, etc. as well as all tags as a
277288
-- Lua table (`object.tags`).
@@ -322,15 +333,15 @@ function osm2pgsql.process_node(object)
322333
elseif object.tags.indoor == "toilet" or object.tags.indoor == "shower" then
323334
-- Point geometry has no area, so synthesize `area = 0` (the icon does not use it). No
324335
-- name/ref: these render as icons only.
336+
local wc = toilet_flags(object.tags)
325337
for _, level in ipairs(SantiseLevel(object.tags.level)) do
326338
tables.pois:insert(
327339
{
328340
indoor = object.tags.indoor,
329341
students_have_access = object.tags.access ~= "private" and object.tags.access ~= "no",
330-
is_male_toilet = object.tags.indoor == "toilet" and object.tags.male == "yes",
331-
is_female_toilet = object.tags.indoor == "toilet" and object.tags.female == "yes",
332-
is_unisex_toilet = object.tags.indoor == "toilet" and object.tags.unisex == "yes",
333-
is_wheelchair_toilet = object.tags.indoor == "toilet" and object.tags.wheelchair == "yes",
342+
is_male_toilet = wc.male,
343+
is_female_toilet = wc.female,
344+
is_wheelchair_toilet = wc.wheelchair,
334345
area = 0,
335346
level_min = level.min,
336347
level_max = level.max,
@@ -378,6 +389,7 @@ function osm2pgsql.process_way(object)
378389
for _, level in ipairs(SantiseLevel(object.tags.level)) do
379390
object.tags.level_min = level.min
380391
object.tags.level_max = level.max
392+
local wc = toilet_flags(object.tags)
381393
-- Very simple check to decide whether a way is a polygon or not, in a
382394
-- real stylesheet we'd have to also look at the tags...
383395
if object.is_closed then
@@ -388,10 +400,9 @@ function osm2pgsql.process_way(object)
388400
indoor = object.tags.indoor,
389401
ref_tum = object.tags["ref:tum"],
390402
students_have_access = object.tags.access ~= "private" and object.tags.access ~= "no",
391-
is_male_toilet = object.tags.indoor == "toilet" and object.tags.male == "yes",
392-
is_female_toilet = object.tags.indoor == "toilet" and object.tags.female == "yes",
393-
is_unisex_toilet = object.tags.indoor == "toilet" and object.tags.unisex == "yes",
394-
is_wheelchair_toilet = object.tags.indoor == "toilet" and object.tags.wheelchair == "yes",
403+
is_male_toilet = wc.male,
404+
is_female_toilet = wc.female,
405+
is_wheelchair_toilet = wc.wheelchair,
395406
level_min = level.min,
396407
level_max = level.max,
397408
geom = geom
@@ -413,10 +424,9 @@ function osm2pgsql.process_way(object)
413424
name = object.tags.name,
414425
ref = object.tags.ref,
415426
students_have_access = object.tags.access ~= "private" and object.tags.access ~= "no",
416-
is_male_toilet = object.tags.indoor == "toilet" and object.tags.male == "yes",
417-
is_female_toilet = object.tags.indoor == "toilet" and object.tags.female == "yes",
418-
is_unisex_toilet = object.tags.indoor == "toilet" and object.tags.unisex == "yes",
419-
is_wheelchair_toilet = object.tags.indoor == "toilet" and object.tags.wheelchair == "yes",
427+
is_male_toilet = wc.male,
428+
is_female_toilet = wc.female,
429+
is_wheelchair_toilet = wc.wheelchair,
420430
area = geom:spherical_area(),
421431
level_min = level.min,
422432
level_max = level.max,
@@ -452,17 +462,17 @@ function osm2pgsql.process_relation(object)
452462
if object.tags.type == "multipolygon" or
453463
object.tags.type == "boundary" then
454464
local geom = object:as_multipolygon()
465+
local wc = toilet_flags(object.tags)
455466
for _, level in ipairs(SantiseLevel(object.tags.level)) do
456467
if object.tags.indoor ~= "stairs" then
457468
tables.rooms:insert(
458469
{
459470
indoor = object.tags.indoor,
460471
ref_tum = object.tags["ref:tum"],
461472
students_have_access = object.tags.access ~= "private" and object.tags.access ~= "no",
462-
is_male_toilet = object.tags.indoor == "toilet" and object.tags.male == "yes",
463-
is_female_toilet = object.tags.indoor == "toilet" and object.tags.female == "yes",
464-
is_unisex_toilet = object.tags.indoor == "toilet" and object.tags.unisex == "yes",
465-
is_wheelchair_toilet = object.tags.indoor == "toilet" and object.tags.wheelchair == "yes",
473+
is_male_toilet = wc.male,
474+
is_female_toilet = wc.female,
475+
is_wheelchair_toilet = wc.wheelchair,
466476
level_min = level.min,
467477
level_max = level.max,
468478
geom = geom
@@ -487,10 +497,9 @@ function osm2pgsql.process_relation(object)
487497
indoor = object.tags.indoor,
488498
ref = object.tags.ref,
489499
students_have_access = object.tags.access ~= "private" and object.tags.access ~= "no",
490-
is_male_toilet = object.tags.indoor == "toilet" and object.tags.male,
491-
is_female_toilet = object.tags.indoor == "toilet" and object.tags.female,
492-
is_unisex_toilet = object.tags.indoor == "toilet" and object.tags.unisex,
493-
is_wheelchair_toilet = object.tags.indoor == "toilet" and object.tags.wheelchair == "yes",
500+
is_male_toilet = wc.male,
501+
is_female_toilet = wc.female,
502+
is_wheelchair_toilet = wc.wheelchair,
494503
area = g:area(),
495504
level_min = level.min,
496505
level_max = level.max,

server/migrations/20260611140000_drop_pois_is_shower.sql

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ BEGIN
2727
students_have_access,
2828
is_male_toilet,
2929
is_female_toilet,
30-
is_unisex_toilet,
3130
is_wheelchair_toilet,
3231
area
3332
FROM pois
@@ -56,7 +55,6 @@ DO $do$ BEGIN
5655
"students_have_access": "Boolean",
5756
"is_male_toilet": "Boolean",
5857
"is_female_toilet": "Boolean",
59-
"is_unisex_toilet": "Boolean",
6058
"is_wheelchair_toilet": "Boolean",
6159
"area": "Real"
6260
},

server/migrations/20260611160000_rooms_wc_attrs.sql

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
ALTER TABLE IF EXISTS rooms
1111
ADD COLUMN IF NOT EXISTS is_male_toilet boolean NOT NULL DEFAULT false,
1212
ADD COLUMN IF NOT EXISTS is_female_toilet boolean NOT NULL DEFAULT false,
13-
ADD COLUMN IF NOT EXISTS is_unisex_toilet boolean NOT NULL DEFAULT false,
1413
ADD COLUMN IF NOT EXISTS is_wheelchair_toilet boolean NOT NULL DEFAULT false;
1514

1615
CREATE OR REPLACE
@@ -31,7 +30,6 @@ BEGIN
3130
students_have_access,
3231
is_male_toilet,
3332
is_female_toilet,
34-
is_unisex_toilet,
3533
is_wheelchair_toilet
3634
FROM rooms
3735
WHERE geom && ST_TileEnvelope(z, x, y) AND
@@ -58,7 +56,6 @@ DO $do$ BEGIN
5856
"students_have_access": "Boolean",
5957
"is_male_toilet": "Boolean",
6058
"is_female_toilet": "Boolean",
61-
"is_unisex_toilet": "Boolean",
6259
"is_wheelchair_toilet": "Boolean"
6360
},
6461
"maxzoom": 30,

tests/specs/map.ui.spec.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ function styleWithToilet(flags: Record<string, boolean>) {
1616
const allFlags = {
1717
is_male_toilet: false,
1818
is_female_toilet: false,
19-
is_unisex_toilet: false,
2019
is_wheelchair_toilet: false,
2120
is_shower: false,
2221
...flags,
@@ -285,31 +284,23 @@ test.describe("Browse map (/map)", () => {
285284
await expect(page.getByRole("checkbox", { name: "Nur rollstuhlgerecht" })).toBeHidden();
286285
});
287286

288-
test("the wheelchair-only filter hides non-accessible toilets", async ({ page }) => {
287+
test("a non-matching toilet stays clickable under the wheelchair-only filter", async ({
288+
page,
289+
}) => {
289290
await stubBasemap(page, styleWithToilet({ is_male_toilet: true }));
290291

291-
await page.goto("/map?filter=wcs", { waitUntil: "networkidle" });
292-
await expect(page.getByRole("region", { name: "Map" })).toBeVisible();
293-
await page.locator("#map-browse canvas").first().click();
294-
await expect(page.locator(".maplibregl-popup-content")).toContainText("Toilette");
295-
296292
await page.goto("/map?filter=wcs&wcs_wheelchair=true", { waitUntil: "networkidle" });
297293
await expect(page.getByRole("region", { name: "Map" })).toBeVisible();
298294
await page.locator("#map-browse canvas").first().click();
299-
await expect(page.locator(".maplibregl-popup-content")).toHaveCount(0);
295+
await expect(page.locator(".maplibregl-popup-content")).toContainText("Toilette");
300296
});
301297

302-
test("the gender filter shows only matching toilets", async ({ page }) => {
298+
test("a non-matching toilet stays clickable under the gender filter", async ({ page }) => {
303299
await stubBasemap(page, styleWithToilet({ is_female_toilet: true }));
304300

305301
await page.goto("/map?filter=wcs&wcs_gender=male", { waitUntil: "networkidle" });
306302
await expect(page.getByRole("region", { name: "Map" })).toBeVisible();
307303
await page.locator("#map-browse canvas").first().click();
308-
await expect(page.locator(".maplibregl-popup-content")).toHaveCount(0);
309-
310-
await page.goto("/map?filter=wcs&wcs_gender=female", { waitUntil: "networkidle" });
311-
await expect(page.getByRole("region", { name: "Map" })).toBeVisible();
312-
await page.locator("#map-browse canvas").first().click();
313304
await expect(page.locator(".maplibregl-popup-content")).toContainText("Damen");
314305
});
315306

webclient/app/composables/mapLayers.ts

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -219,12 +219,21 @@ export const WCS_GENDER_QUERY_PARAM = "wcs_gender";
219219
export const WCS_GENDERS = ["male", "female", "unisex"] as const;
220220
export type WcsGender = (typeof WCS_GENDERS)[number];
221221

222-
/** The tile property carrying each gender's flag. */
222+
/** The tile property carrying each single-gender flag. Unisex has no flag of its own. */
223223
const WCS_GENDER_FLAG = {
224224
male: "is_male_toilet",
225225
female: "is_female_toilet",
226-
unisex: "is_unisex_toilet",
227-
} as const satisfies Record<WcsGender, string>;
226+
} as const satisfies Record<Exclude<WcsGender, "unisex">, string>;
227+
228+
/**
229+
* Per-feature predicate selecting a gender. A unisex/all-gender toilet has no flag of its own; it
230+
* is encoded as both male and female, so the unisex selection matches that pair.
231+
*/
232+
function wcsGenderCondition(gender: WcsGender): JsonExpression {
233+
if (gender === "unisex")
234+
return ["all", ["get", "is_male_toilet"], ["get", "is_female_toilet"]];
235+
return ["get", WCS_GENDER_FLAG[gender]];
236+
}
228237

229238
/** Parse a `?wcs_gender=` value, or `null` when absent or not a known gender. */
230239
export function parseWcsGender(param: string | null | undefined): WcsGender | null {
@@ -246,38 +255,17 @@ export type JsonExpression = readonly (string | number | boolean | JsonExpressio
246255
/**
247256
* Per-feature `["get", flag]` predicates for each WC attribute the user selected. A WC feature
248257
* "matches" when every returned predicate is truthy on it. Empty when nothing is selected.
249-
* Shared between the POI hide-filter and the room background dim so the two stay in lockstep.
250258
*/
251259
export function wcsAttributeConditions(opts: {
252260
wheelchair: boolean;
253261
gender: WcsGender | null;
254262
}): JsonExpression[] {
255263
const conditions: JsonExpression[] = [];
256264
if (opts.wheelchair) conditions.push(["get", "is_wheelchair_toilet"]);
257-
if (opts.gender) conditions.push(["get", WCS_GENDER_FLAG[opts.gender]]);
265+
if (opts.gender) conditions.push(wcsGenderCondition(opts.gender));
258266
return conditions;
259267
}
260268

261-
/**
262-
* Style filter for the shared indoor-POI layer hiding the WC markers that do not match the
263-
* selected attributes. Non-WC features (elevators etc.) pass unconditionally; WC features must
264-
* carry every selected flag. Showers carry no gender or wheelchair flag, so any active attribute
265-
* filter hides them with the non-matching toilets. Returns `null` when no attribute is selected,
266-
* signalling the caller to restore the layer's own filter.
267-
*/
268-
export function wcsAttributeFilter(opts: {
269-
wheelchair: boolean;
270-
gender: WcsGender | null;
271-
}): JsonExpression | null {
272-
const conditions = wcsAttributeConditions(opts);
273-
if (conditions.length === 0) return null;
274-
return [
275-
"any",
276-
["!", ["in", ["get", "indoor"], ["literal", [...WCS_INDOOR_VALUES]]]],
277-
["all", ...conditions],
278-
];
279-
}
280-
281269
/** Parse a `?level=` value into a known integer floor, or `null` when absent or invalid. */
282270
export function parseLevel(param: string | null | undefined): number | null {
283271
if (param === null || param === undefined || param.trim() === "") return null;

webclient/app/pages/map.vue

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ import {
3535
WCS_WHEELCHAIR_QUERY_PARAM,
3636
type WcsGender,
3737
wcsAttributeConditions,
38-
wcsAttributeFilter,
3938
} from "~/composables/mapLayers";
4039
import { useEventPopup } from "~/composables/useEventMarkers";
4140
import { useWebglGuard } from "~/composables/webglSupport";
@@ -103,9 +102,6 @@ const { activeEvent, markerScreenPos, closeActiveEvent } = useEventPopup(map, EV
103102
104103
// Original paint values captured before the first dim, so toggling a filter off restores them.
105104
const originalPaint = new Map<string, OpacityValue | undefined>();
106-
// The POI layer's own style filter, captured before the first attribute filter so clearing the
107-
// attributes restores it. `undefined` = not captured yet; `null` = the layer had no filter.
108-
let originalPoiFilter: FilterSpecification | null | undefined;
109105
110106
/** Read a query value as a single string, distinguishing "absent" (null) from "present but empty" (""). */
111107
function queryString(key: string): string | null {
@@ -204,20 +200,6 @@ function applyEventsFilter(): void {
204200
m.setFilter(EVENTS_STYLE_LAYER, filter as FilterSpecification);
205201
}
206202
207-
/**
208-
* Hide the WC markers not matching the selected attributes while the WCs filter is active;
209-
* restore the layer's own filter when it is not (or when no attribute is selected).
210-
*/
211-
function applyWcsAttributeFilter(): void {
212-
const m = map.value;
213-
if (!m?.getLayer(POI_LAYER)) return;
214-
if (originalPoiFilter === undefined) originalPoiFilter = m.getFilter(POI_LAYER) ?? null;
215-
const expr = activeFilters.value.has(WCS_FILTER_ID)
216-
? wcsAttributeFilter({ wheelchair: wcsWheelchair.value, gender: wcsGender.value })
217-
: null;
218-
m.setFilter(POI_LAYER, (expr as FilterSpecification | null) ?? originalPoiFilter);
219-
}
220-
221203
function toggleFilter(id: string): void {
222204
const next = new Set(activeFilters.value);
223205
if (next.has(id)) next.delete(id);
@@ -232,7 +214,6 @@ watch(activeFilters, (filters) => {
232214
setQueryParam(FILTER_QUERY_PARAM, serialized || null);
233215
applyFilterDim();
234216
applyOverlayVisibility();
235-
applyWcsAttributeFilter();
236217
// The popup belongs to the events layer; it must not outlive the layer being switched off.
237218
if (!filters.has(EVENTS_FILTER_ID)) closeActiveEvent();
238219
});
@@ -248,7 +229,6 @@ watch([wcsWheelchair, wcsGender], ([wheelchair, gender]) => {
248229
// Drop each param at its "no condition" default, so a bare /map URL stays clean.
249230
setQueryParam(WCS_WHEELCHAIR_QUERY_PARAM, wheelchair ? "true" : null);
250231
setQueryParam(WCS_GENDER_QUERY_PARAM, gender);
251-
applyWcsAttributeFilter();
252232
applyFilterDim();
253233
});
254234
@@ -284,10 +264,15 @@ function buildPopupContent(feature: MapGeoJSONFeature, lng: number, lat: number)
284264
};
285265
286266
if (!isShower) {
267+
const male = truthy(props.is_male_toilet);
268+
const female = truthy(props.is_female_toilet);
269+
// A toilet serving both is all-gender; show it as unisex rather than "male, female".
287270
const genders: string[] = [];
288-
if (truthy(props.is_unisex_toilet)) genders.push(t("gender.unisex"));
289-
if (truthy(props.is_male_toilet)) genders.push(t("gender.male"));
290-
if (truthy(props.is_female_toilet)) genders.push(t("gender.female"));
271+
if (male && female) genders.push(t("gender.unisex"));
272+
else {
273+
if (male) genders.push(t("gender.male"));
274+
if (female) genders.push(t("gender.female"));
275+
}
291276
if (genders.length) addRow(`${t("gender.label")}: ${genders.join(", ")}`);
292277
if (truthy(props.is_wheelchair_toilet)) addRow(t("wheelchair_accessible"));
293278
}
@@ -372,7 +357,6 @@ function initMap(): MapLibreMap {
372357
applyFilterDim();
373358
applyOverlayVisibility();
374359
applyEventsFilter();
375-
applyWcsAttributeFilter();
376360
377361
m.on("click", POI_LAYER, openPoiPopup);
378362
m.on("mouseenter", POI_LAYER, () => {

0 commit comments

Comments
 (0)