Skip to content

Commit 8ceae21

Browse files
committed
Add same line matching question
It would not be a stretch to say that this question does not warrant existence. Some times train stations that aren't on the line are allegedly on the line and vice versa. A better API would improve this significantly. Thankfully hider mode prevents contradictions, but this should really be refined.
1 parent 0d3d557 commit 8ceae21

4 files changed

Lines changed: 110 additions & 7 deletions

File tree

src/components/QuestionCards.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,16 @@ export const MatchingQuestionComponent = ({
299299
)}
300300
</>
301301
);
302+
break;
303+
case "same-train-line":
304+
questionSpecific = (
305+
<span className="px-2 text-center text-orange-500">
306+
Warning: The train line data is based on OpenStreetMap and
307+
may have fewer train stations than expected. If you are
308+
using this tool, assure that the other players are also
309+
using this tool.
310+
</span>
311+
);
302312
}
303313

304314
return (
@@ -344,6 +354,13 @@ export const MatchingQuestionComponent = ({
344354
Station Has Same Length Question (must be in hiding
345355
zone mode)
346356
</SelectItem>
357+
<SelectItem
358+
value="same-train-line"
359+
disabled={!$displayHidingZones}
360+
>
361+
Station On Same Train Line Question (must be in
362+
hiding zone mode)
363+
</SelectItem>
347364
</SelectContent>
348365
</Select>
349366
</SidebarMenuItem>

src/components/ZoneSidebar.tsx

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { Label } from "./ui/label";
1818
import { Checkbox } from "./ui/checkbox";
1919
import { useEffect, useState } from "react";
2020
import { geoJSON } from "leaflet";
21-
import { findPlacesInZone } from "@/maps/api";
21+
import { findPlacesInZone, trainLineNodeFinder } from "@/maps/api";
2222
import * as turf from "@turf/turf";
2323
import osmtogeojson from "osmtogeojson";
2424
import { unionize } from "@/maps/geo-utils";
@@ -63,11 +63,12 @@ export const ZoneSidebar = () => {
6363
),
6464
).features;
6565

66-
$questions.forEach((question) => {
66+
for (const question of $questions) {
6767
if (
6868
question.id === "matching" &&
6969
(question.data.type === "same-first-letter-station" ||
70-
question.data.type === "same-length-station")
70+
question.data.type === "same-length-station" ||
71+
question.data.type === "same-train-line")
7172
) {
7273
const location = turf.point([
7374
question.data.lng,
@@ -79,6 +80,29 @@ export const ZoneSidebar = () => {
7980
turf.featureCollection(places) as any,
8081
);
8182

83+
if (question.data.type === "same-train-line") {
84+
const nodes = await trainLineNodeFinder(
85+
nearestTrainStation.properties.id,
86+
);
87+
88+
if (nodes.length === 0) {
89+
toast.warning(
90+
`No train line found for ${nearestTrainStation.properties["name:en"] || nearestTrainStation.properties.name}`,
91+
);
92+
continue;
93+
} else {
94+
places = places.filter((place: any) => {
95+
const id = parseInt(
96+
place.properties.id.split("/")[1],
97+
);
98+
99+
return question.data.same
100+
? nodes.includes(id)
101+
: !nodes.includes(id);
102+
});
103+
}
104+
}
105+
82106
const englishName =
83107
nearestTrainStation.properties["name:en"] ||
84108
nearestTrainStation.properties.name;
@@ -116,7 +140,7 @@ export const ZoneSidebar = () => {
116140
});
117141
}
118142
}
119-
});
143+
}
120144

121145
const unionized = unionize($questionFinishedMapData)!;
122146

src/maps/api.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,43 @@ export const fetchCoastline = async () => {
222222
return data;
223223
};
224224

225+
export const trainLineNodeFinder = async (node: string): Promise<number[]> => {
226+
const nodeId = node.split("/")[1];
227+
228+
const query = `
229+
[out:json];
230+
node(${nodeId});
231+
wr(bn);
232+
out geom;
233+
`;
234+
235+
const data = await getOverpassData(query, "Finding train lines...");
236+
237+
const geoJSON = osmtogeojson(data);
238+
239+
const nodes: number[] = [];
240+
241+
geoJSON.features.forEach((feature: any) => {
242+
// For relations
243+
if (feature && feature.id && feature.id.startsWith("node")) {
244+
nodes.push(parseInt(feature.id.split("/")[1]));
245+
}
246+
});
247+
248+
data.elements.forEach((element: any) => {
249+
// For ways
250+
if (element && element.type === "node") {
251+
nodes.push(element.id);
252+
} else if (element && element.type === "way") {
253+
nodes.push(...element.nodes);
254+
}
255+
});
256+
257+
const uniqNodes = _.uniq(nodes);
258+
259+
return uniqNodes;
260+
};
261+
225262
export const findPlacesInZone = async (
226263
filter: string,
227264
loadingText?: string,

src/maps/matching.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { hiderMode, mapGeoJSON, questions } from "@/lib/context";
2-
import { findAdminBoundary, findPlacesInZone, iconColors } from "./api";
2+
import { findAdminBoundary, findPlacesInZone, iconColors, trainLineNodeFinder } from "./api";
33
import * as turf from "@turf/turf";
44
import type { LatLng } from "leaflet";
55
import _ from "lodash";
@@ -39,6 +39,10 @@ export interface SameLengthStationMatchingQuestion
3939
type: "same-length-station";
4040
}
4141

42+
export interface SameTrainLineMatchingQuestion extends BaseMatchingQuestion {
43+
type: "same-train-line";
44+
}
45+
4246
export interface LetterMatchingZoneQuestion extends BaseMatchingQuestion {
4347
type: "letter-zone";
4448
cat: MatchingZoneQuestion;
@@ -49,7 +53,8 @@ export type MatchingQuestion =
4953
| AirportMatchingQuestion
5054
| LetterMatchingZoneQuestion
5155
| SameFirstLetterStationMatchingQuestion
52-
| SameLengthStationMatchingQuestion;
56+
| SameLengthStationMatchingQuestion
57+
| SameTrainLineMatchingQuestion;
5358

5459
export const adjustPerMatching = async (
5560
question: MatchingQuestion,
@@ -73,6 +78,9 @@ export const adjustPerMatching = async (
7378
case "same-length-station": {
7479
return mapData;
7580
}
81+
case "same-train-line": {
82+
return mapData;
83+
}
7684
case "zone": {
7785
boundary = await findAdminBoundary(
7886
question.lat,
@@ -211,7 +219,8 @@ export const hiderifyMatching = async (question: MatchingQuestion) => {
211219

212220
if (
213221
question.type === "same-first-letter-station" ||
214-
question.type === "same-length-station"
222+
question.type === "same-length-station" ||
223+
question.type === "same-train-line"
215224
) {
216225
const hiderPoint = turf.point([
217226
$hiderMode.longitude,
@@ -236,6 +245,22 @@ export const hiderifyMatching = async (question: MatchingQuestion) => {
236245
places as any,
237246
);
238247

248+
if (question.type === "same-train-line") {
249+
const nodes = await trainLineNodeFinder(
250+
nearestSeekerTrainStation.properties.id,
251+
);
252+
253+
const hiderId = parseInt(
254+
nearestHiderTrainStation.properties.id.split("/")[1],
255+
);
256+
257+
if (nodes.includes(hiderId)) {
258+
question.same = true;
259+
} else {
260+
question.same = false;
261+
}
262+
}
263+
239264
const hiderEnglishName =
240265
nearestHiderTrainStation.properties["name:en"] ||
241266
nearestHiderTrainStation.properties.name;

0 commit comments

Comments
 (0)