crs-l2-fiji-antimeridian
Map (reference ← swipe → agent)
0:00
I'm pulling together per-transect coverage figures for our survey report, but some of the lines in fiji_transects_wgs84.geojson render as arcs spanning the entire globe instead of short local segments. Can you fix the geometries so they render correctly, convert them to Fiji's national metric grid, and give me each transect as one MultiLineString feature with a length_m attribute in metres?
Every feature must come out as a MultiLineString, so do not leave any transects as plain LineString, and the problematic ones must end up as multi-part geometries that faithfully represent the actual survey path. Carry the survey attributes (vessel and survey_date) through unchanged, use transect_id as the key field, and write the result as GeoJSON named fiji_transects_fmg.geojson.
0:00
System info
{
"adapter": "openrouter",
"model": "deepseek/deepseek-v4-flash",
"base_url": "https://openrouter.ai/api/v1"
}
0:03
Let me start by examining the input file to understand the data and identify the problematic geometries.
0:03
Read
/work/fiji_transects_wgs84.geojson
1 {
2 "type": "FeatureCollection",
3 "name": "fiji_transects_wgs84",
4 "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
5 "features": [
6 { "type": "Feature", "properties": { "transect_id": "T001", "vessel": "Taveuni II", "survey_date": "2025-08-15", "crosses_antimeridian_flag": true }, "geometry": { "type": "LineString", "coordinates": [ [ 178.941879580782086, -17.632301816020771 ], [ 179.456655009654696, -17.623768593111961 ], [ 179.971430438527307, -17.611072392823225 ], [ -179.513794132600054, -17.596198176111951 ], [ -178.999018703727444, -17.579632069513806 ], [ -178.484243274854833, -17.577321872344196 ] ] } },
7 { "type": "Feature", "properties": { "transect_id": "T002", "vessel": "Bligh", "survey_date": "2025-08-12", "crosses_antimeridian_flag": true }, "geometry": { "type": "LineString", "coordinates": [ [ 179.070288824800542, -17.334767984150709 ], [ 179.725942881740252, -17.171343988196945 ], [ -179.618403061320009, -17.007726843860763 ], [ -178.962749004380299, -16.834850408534852 ] ] } },
8 { "type": "Feature", "properties": { "transect_id": "T003", "vessel": "Lomaiviti", "survey_date": "2025-08-12", "crosses_antimeridian_flag": true }, "geometry": { "type": "LineString", "coordinates": [ [ 177.636947428758333, -17.358837040696127 ], [ 178.462815605623859, -17.416074837434927 ], [ 179.288683782489386, -17.471713663377283 ], [ -179.885448040645059, -17.527675804260884 ], [ -179.059579863779533, -17.590577543375694 ] ] } },
9 { "type": "Feature", "properties": { "transect_id": "T004", "vessel": "Vanua I", "survey_date": "2025-08-15", "crosses_antimeridian_flag": true }, "geometry": { "type": "LineString", "coordinates": [ [ 178.873696381462679, -17.803295238757038 ], [ 179.542254331047587, -17.598477321703708 ], [ -179.789187719367533, -17.405549472537043 ], [ -179.120629769782624, -17.19411411231702 ], [ -178.452071820197745, -16.992332922174427 ], [ -177.783513870612836, -16.795171196845544 ] ] } },
10 { "type": "Feature", "properties": { "transect_id": "T005", "vessel": "Bligh", "survey_date": "2025-08-19", "crosses_antimeridian_flag": true }, "geometry": { "type": "LineString", "coordinates": [ [ 177.596924830927293, -17.923255155112717 ], [ 178.160696712947697, -18.010328270600898 ], [ 178.724468594968101, -18.099224972140938 ], [ 179.288240476988506, -18.17532145144995 ], [ 179.85201235900891, -18.270380145331412 ], [ -179.584215758970686, -18.358875431719703 ], [ -179.020443876950281, -18.437390943572723 ] ] } },
11 { "type": "Feature", "properties": { "transect_id": "T006", "vessel": "Cakaulevu", "survey_date": "2025-08-13", "crosses_antimeridian_flag": true }, "geometry": { "type": "LineString", "coordinates": [ [ 178.622166647099931, -18.382052393687179 ], [ 179.205668025282989, -18.184610858038685 ], [ 179.789169403466019, -17.991866703295319 ], [ -179.627329218350923, -17.789532271264445 ], [ -179.043827840167864, -17.595682751178369 ], [ -178.460326461984835, -17.393483000040479 ], [ -177.876825083801776, -17.203490564162109 ] ] } },
12 { "type": "Feature", "properties": { "transect_id": "T007", "vessel": "Vanua I", "survey_date": "2025-08-19", "crosses_antimeridian_flag": true }, "geometry": { "type": "LineString", "coordinates": [ [ 177.920101619592742, -17.14979850360189 ], [ 178.697177550619955, -17.100522188212071 ], [ 179.474253481647168, -17.053867486482115 ], [ -179.74867058732562, -17.004197778897627 ], [ -178.971594656298407, -16.959481244370728 ], [ -178.194518725271195, -16.911697858015476 ] ] } },
13 { "type": "Feature", "properties": { "transect_id": "T008", "vessel": "Taveuni II", "survey_date": "2025-08-12", "crosses_antimeridian_flag": true }, "geometry": { "type": "LineString", "coordinates": [ [ 179.401134548056262, -17.934415957637167 ], [ 179.67454249485786, -17.899654147497863 ], [ 179.947950441659486, -17.858130727687527 ], [ -179.778641611538916, -17.811143619533269 ], [ -179.50523366473729, -17.768768396969666 ], [ -179.231825717935692, -17.724708363613253 ] ] } },
14 { "type": "Feature", "properties": { "transect_id": "T009", "vessel": "Vanua I", "survey_date": "2025-08-13", "crosses_antimeridian_flag": true }, "geometry": { "type": "LineString", "coordinates": [ [ 178.068433112639013, -16.657838811131668 ], [ 178.561564721122721, -16.689731320158675 ], [ 179.054696329606429, -16.72173241552732 ], [ 179.547827938090137, -16.748891897767297 ], [ -179.959040453426155, -16.775241845025921 ], [ -179.465908844942447, -16.799281899607834 ] ] } },
15 { "type": "Feature", "properties": { "transect_id": "T010", "vessel": "Vanua I", "survey_date": "2025-08-15", "crosses_antimeridian_flag": true }, "geometry": { "type": "LineString", "coordinates": [ [ 177.602847396853463, -17.277026295424367 ], [ 179.128053569323441, -17.468285296635063 ], [ -179.346740258206609, -17.65419136085664 ], [ -177.82153408573663, -17.837325774502055 ] ] } },
16 { "type": "Feature", "properties": { "transect_id": "T011", "vessel": "Taveuni II", "survey_date": "2025-08-20", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ -176.863797870333627, -17.7932433540029 ], [ -176.985724572324699, -17.81273654528005 ], [ -177.107651274315742, -17.824236921378098 ], [ -177.229577976306814, -17.835861250474697 ], [ -177.351504678297886, -17.857559546831705 ], [ -177.473431380288929, -17.871769203842408 ], [ -177.595358082280001, -17.890225470404214 ] ] } },
17 { "type": "Feature", "properties": { "transect_id": "T012", "vessel": "Taveuni II", "survey_date": "2025-08-20", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ 176.312195951017685, -17.976122209608448 ], [ 176.362236324041447, -17.722210270969462 ], [ 176.412276697065209, -17.470306839752467 ], [ 176.462317070088972, -17.226475769084448 ] ] } },
18 { "type": "Feature", "properties": { "transect_id": "T013", "vessel": "Bligh", "survey_date": "2025-08-19", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ -176.70823848972006, -17.696306145080879 ], [ -177.205026853648661, -17.960082567744127 ], [ -177.701815217577291, -18.210737758337459 ], [ -178.198603581505893, -18.469124252379427 ] ] } },
19 { "type": "Feature", "properties": { "transect_id": "T014", "vessel": "Lomaiviti", "survey_date": "2025-08-13", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ 176.538496893308832, -16.594358164012139 ], [ 176.861572490827996, -16.71494251806207 ], [ 177.184648088347188, -16.832251271240672 ], [ 177.507723685866381, -16.976116506165042 ], [ 177.830799283385545, -17.090939089364483 ] ] } },
20 { "type": "Feature", "properties": { "transect_id": "T015", "vessel": "Vanua I", "survey_date": "2025-08-15", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ -177.278377449219306, -16.724372731501912 ], [ -177.470251917030794, -16.761535677586746 ], [ -177.662126384842253, -16.787182414326686 ], [ -177.854000852653712, -16.818799270133983 ], [ -178.0458753204652, -16.856282470705128 ], [ -178.237749788276659, -16.891308864567598 ], [ -178.429624256088147, -16.914729063096839 ] ] } },
21 { "type": "Feature", "properties": { "transect_id": "T016", "vessel": "Taveuni II", "survey_date": "2025-08-15", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ 178.445325115686984, -18.225562645051426 ], [ 178.570919872630384, -18.22187730118792 ], [ 178.696514629573755, -18.228085980840198 ], [ 178.822109386517127, -18.243164774607269 ], [ 178.947704143460527, -18.232257588046291 ] ] } },
22 { "type": "Feature", "properties": { "transect_id": "T017", "vessel": "Taveuni II", "survey_date": "2025-08-12", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ -176.949497668404376, -16.593233226112279 ], [ -177.073130399044231, -16.796136633276152 ], [ -177.196763129684086, -16.985064764093782 ], [ -177.320395860323913, -17.188172387851619 ], [ -177.444028590963768, -17.380278567766066 ], [ -177.567661321603623, -17.571298263401527 ], [ -177.691294052243478, -17.76827441812642 ] ] } },
23 { "type": "Feature", "properties": { "transect_id": "T018", "vessel": "Bligh", "survey_date": "2025-08-19", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ 176.111138114901024, -16.851117503263293 ], [ 176.291399614433374, -17.034309766495799 ], [ 176.471661113965695, -17.221302006070296 ], [ 176.651922613498016, -17.402317112686557 ], [ 176.832184113030365, -17.592994204367653 ] ] } },
24 { "type": "Feature", "properties": { "transect_id": "T019", "vessel": "Lomaiviti", "survey_date": "2025-08-12", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ 178.007516628261584, -17.706954766350957 ], [ 177.596962177324428, -17.607161226082525 ], [ 177.186407726387301, -17.511279680346377 ], [ 176.775853275450146, -17.418568907300433 ], [ 176.36529882451299, -17.326891370330944 ] ] } },
25 { "type": "Feature", "properties": { "transect_id": "T020", "vessel": "Taveuni II", "survey_date": "2025-08-13", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ 176.869640838143511, -17.085093978662378 ], [ 177.34023164697706, -17.269542693344249 ], [ 177.810822455810609, -17.442447410976666 ], [ 178.281413264644158, -17.624621823987695 ], [ 178.752004073477707, -17.807162082048386 ] ] } },
26 { "type": "Feature", "properties": { "transect_id": "T021", "vessel": "Vanua I", "survey_date": "2025-08-16", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ 176.258661225850091, -17.092877710432774 ], [ 176.830345357965598, -17.521807453385343 ], [ 177.402029490081105, -17.943438299004075 ], [ 177.973713622196613, -18.366984215963161 ] ] } },
27 { "type": "Feature", "properties": { "transect_id": "T022", "vessel": "Vanua I", "survey_date": "2025-08-15", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ 177.115699442929383, -17.820293640195572 ], [ 177.180354072206086, -17.755581112632651 ], [ 177.245008701482789, -17.684692899482695 ], [ 177.30966333075952, -17.61995755685173 ], [ 177.374317960036223, -17.544174647257226 ], [ 177.438972589312925, -17.479955835030527 ] ] } },
28 { "type": "Feature", "properties": { "transect_id": "T023", "vessel": "Lomaiviti", "survey_date": "2025-08-12", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ -177.191351883954098, -17.679938619650844 ], [ -177.334313691261116, -17.740394590021616 ], [ -177.477275498568133, -17.794367957957562 ], [ -177.62023730587515, -17.835119945356809 ], [ -177.763199113182168, -17.904282380225705 ], [ -177.906160920489185, -17.955635131096855 ] ] } },
29 { "type": "Feature", "properties": { "transect_id": "T024", "vessel": "Vanua I", "survey_date": "2025-08-12", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ -179.108274205314615, -16.84950325482971 ], [ -178.651703763804733, -16.962456436762313 ], [ -178.195133322294822, -17.077410225872036 ], [ -177.73856288078494, -17.187966865781235 ], [ -177.281992439275029, -17.288042922581088 ], [ -176.825421997765147, -17.40904519070677 ] ] } },
30 { "type": "Feature", "properties": { "transect_id": "T025", "vessel": "Taveuni II", "survey_date": "2025-08-19", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ 176.574749815324452, -18.225329195672629 ], [ 176.575711406506713, -17.903825702399828 ], [ 176.576672997688945, -17.572376190438085 ], [ 176.577634588871206, -17.245872893430239 ] ] } },
31 { "type": "Feature", "properties": { "transect_id": "T026", "vessel": "Cakaulevu", "survey_date": "2025-08-19", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ 178.630248956242411, -17.547785158041279 ], [ 178.44215451378426, -17.570057172060459 ], [ 178.254060071326109, -17.598148620651486 ], [ 178.065965628867986, -17.611819625668215 ], [ 177.877871186409834, -17.645164465599024 ], [ 177.689776743951683, -17.659546383089758 ] ] } },
32 { "type": "Feature", "properties": { "transect_id": "T027", "vessel": "Vanua I", "survey_date": "2025-08-12", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ -177.773526218774975, -17.183972526301677 ], [ -177.993798674497214, -17.290101550005915 ], [ -178.214071130219452, -17.383420029016879 ], [ -178.434343585941718, -17.487055860389813 ], [ -178.654616041663957, -17.584325384194859 ], [ -178.874888497386195, -17.694189986558115 ] ] } },
33 { "type": "Feature", "properties": { "transect_id": "T028", "vessel": "Vanua I", "survey_date": "2025-08-12", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ -178.154921880223981, -16.519500569812283 ], [ -177.760867938660567, -16.61473611015824 ], [ -177.366813997097154, -16.706948361526067 ], [ -176.972760055533769, -16.779276042892469 ], [ -176.578706113970355, -16.874986722588798 ] ] } },
34 { "type": "Feature", "properties": { "transect_id": "T029", "vessel": "Lomaiviti", "survey_date": "2025-08-19", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ -176.563841265411412, -18.091246216590591 ], [ -176.943316553982299, -17.796168551154441 ], [ -177.322791842553215, -17.510273970538908 ], [ -177.702267131124103, -17.231705179840109 ], [ -178.08174241969499, -16.949176694851047 ] ] } },
35 { "type": "Feature", "properties": { "transect_id": "T030", "vessel": "Cakaulevu", "survey_date": "2025-08-19", "crosses_antimeridian_flag": false }, "geometry": { "type": "LineString", "coordinates": [ [ -177.772700226865993, -16.898671712097919 ], [ -177.981739053528088, -17.042373280579991 ], [ -178.190777880190183, -17.175821795383495 ], [ -178.399816706852278, -17.32978967298839 ], [ -178.608855533514344, -17.474383681681747 ], [ -178.817894360176439, -17.613581860280195 ], [ -179.026933186838534, -17.755873350663471 ] ] } }
36 ]
37 }
0:30
Let me analyze the problem. The features with `crosses_antimeridian_flag: true` have coordinates that cross the antimeridian (180° longitude). For example, T001 has coordinates like: - 178.941..., 179.456..., 179.971..., -179.513..., -178.999..., -178.484... The issue is that when rendering software plots these, it sees a jump from 179.971 to -179.513, which goes the "long way" around the globe (across -180 to +180 is a gap of ~359 degrees, but the actual path should be a short segment across the date line). I need to: 1. For features crossing the antimeridian, split the linestring at the antimeridian, creating multi-part geometries 2. Add 360 to the negative longitude values on the "east" side of the antimeridian, or subtract 360 from positive values on the "west" side 3. Then project to Fiji's national metric grid system 4. Calculate length in metres 5. Output as MultiLineString GeoJSON Fiji's national metric grid... Let me think about this. Fiji uses a projection suitable for the Fiji region. The common one would be something like: - EPSG:3460 - Fiji 1956 / Fiji Map Grid - EPSG:3141 - maybe something else - Actually, Fiji Map Grid is EPSG:3460 (Fiji 1956 / Fiji Map Grid) or EPSG:3139 (Fiji 1956 / UTM zone 60S) - But there's also EPSG:2120 (Fiji 1986 / UTM zone 60S) - Actually, the most common "Fiji's national metric grid" is likely "Fiji Map Grid" which is EPSG:3460 (Fiji 1956 / Fiji Map Grid) or EPSG:3138 / 3139 (Fiji 1956 / UTM zones 59S and 60S) - Wait, let me check. Fiji Map Grid is EPSG:3460 (Fiji 1956 / Fiji Map Grid). That's a single grid covering all of Fiji. Actually, let me check what projections are available. But first, let me think about the approach. The key insight is: - For features that cross the antimeridian, the coordinates go from positive longitudes (east of 180°) to negative longitudes (west of 180°) - A naive rendering connects the last point east of the dateline (e.g., 179.97) to the first point west of the dateline (e.g., -179.51) going the long way around - The fix: when points cross 180°, we can normalize coordinates so they're continuous One approach: 1. For each feature, check if it crosses the antimeridian 2. If it does, split the LineString at the antimeridian into segments 3. For coordinates on the "west" side (negative longitudes), add 360 to them to make them continuous 4. Then after projecting, the segments will be separate parts of a MultiLineString Let me think about this more carefully. For T001, the coordinates in longitude order are: 178.94, 179.46, 179.97, -179.51, -179.00, -178.48 So the path goes eastward from ~179°E across the date line to ~179°W (which is 181°E in continuous coordinates), then continues westward to ~178°W (182°E). If we add 360 to the negative longitudes: 178.94, 179.46, 179.97, 180.49, 181.00, 181.52 But that creates one continuous line. However, the problem is about rendering - we need to split it into segments that don't cross the antimeridian. Actually, the best approach is: For features crossing the antimeridian, I should: 1. Identify where the longitude jumps across the antimeridian (from ~179/+ to ~-179/- or vice versa) 2. Split at those points 3. For the segments on each side, keep original coordinates 4. Create a MultiLineString with parts on each side of the antimeridian Then to calculate length in metres, project each part to a suitable CRS and calculate. Let me write a Python script. First, let me check what CRS to use for Fiji's national metric grid. I'll use pyproj to look up available CRS for Fiji.
0:30
0:30
Bash
python3 -c "
python3 -c "
from pyproj import CRS
# Fiji Map Grid
crs = CRS.from_epsg(3460)
print(crs)
print('---')
# Also check EPSG:3139
crs2 = CRS.from_epsg(3139)
print(crs2)
print('---')
crs3 = CRS.from_epsg(2120)
print(crs3)
"
EPSG:3460 --- EPSG:3139 --- EPSG:2120
0:37
Let me check Fiji Map Grid - EPSG:3460 is "Fiji 1956 / Fiji Map Grid" which seems like the right one. But let me also check EPSG:2138 - "Fiji 1956 / UTM zone 60S" or "Fiji 1956 / UTM zone 1S". Actually, the Fiji Map Grid is EPSG:3460 (Fiji 1956 / Fiji Map Grid). Let me verify. Actually, Fiji's national metric grid is likely "Fiji 1956 / Fiji Map Grid" (EPSG:3460) or "Fiji 1986 / Fiji Map Grid" (EPSG:3139). Let me check more. Wait, the user said "Fiji's national metric grid". The most standard one is EPSG:3460 (Fiji 1956 / Fiji Map Grid). But common modern one is EPSG:3139 (Fiji 1986 / Fiji Map Grid). Let me check what's available and commonly used.
0:37
Bash
python3 -c "
python3 -c "
from pyproj import CRS
# Check details
for e in [3460, 3139, 2120, 2138, 2139]:
c = CRS.from_epsg(e)
print(f'EPSG:{e} - {c.name}')
"
EPSG:3460 - Fiji 1986 / Fiji Map Grid EPSG:3139 - Vanua Levu 1915 / Vanua Levu Grid EPSG:2120 - NZGD2000 / Marlborough 2000 EPSG:2138 - NAD27(CGQ77) / Quebec Lambert EPSG:2139 - NAD83(CSRS98) / SCoPQ zone 2
1:16
EPSG:3460 is "Fiji 1986 / Fiji Map Grid" - that's Fiji's national metric grid. Let me write the solution.
Now, the issue with the geometries: transects that cross the antimeridian (the 180° longitude line) have coordinates that go from positive (e.g. 179.9°) to negative (e.g. -179.8°). In a naive rendering, these would connect across the entire globe instead of going the short way across the dateline.
The solution: I need to split these antimeridian-crossing lines at the dateline (180° longitude). For each segment that crosses from one side to the other, I need to split it and create separate parts on each side.
Let me think about the approach:
1. For each feature, check if it crosses the antimeridian (or use the flag)
2. If it does, split the line at the antimeridian (180° longitude)
3. Then reproject to Fiji Map Grid (EPSG:3460)
4. Calculate length
5. Output as MultiLineString
Actually, a simpler approach:
- Shift longitudes that are near the antimeridian: for segments crossing the dateline, I can add/subtract 360 to make them continuous, then split.
Let me think more carefully. The problem is coordinates like [179.9, ...] followed by [-179.9, ...]. If I add 360 to the negative values, I get continuous longitudes. Then I need to split at the 180° meridian.
Actually, the standard approach for antimeridian crossing:
1. For each pair of consecutive points, check if they cross the antimeridian (i.e., one has lon near +180 and other near -180, with a large gap)
2. If so, interpolate the crossing point at lon=180
3. Split the linestring into two parts: one on each side
4. Shift the eastern side's longitudes by -360 to get them back to normal, or shift the western side's by +360
Wait, a better approach:
- For each linestring, iterate through coordinates.
- If a segment crosses the antimeridian (lon diff > 180 in absolute value), compute intersection with the 180° meridian.
- Create two separate linestrings: one for the eastern side (longitudes > 0, but for the crossing part, up to 180) and one for the western side (longitudes < 0, from -180 onward).
Let me think about the algorithm more carefully.
For a line crossing from east to west (e.g., 179.9 -> -179.9):
1. The crossing point at 180° longitude can be interpolated.
2. Part 1: from start to the crossing point (east side)
3. Part 2: from crossing point to end (west side)
But I also need to handle the case where the line goes from west to east (e.g., -179.9 -> 179.9), though in the data I see the antimeridian-crossing ones go east->west.
Let me write a comprehensive solution.
Actually, let me use the `shapely.ops.transform` and `shapely.ops.split` approach, or use a technique with `shapely.set_precision` or manual splitting.
Let me write a Python script:
```python
import json
import geopandas as gpd
import shapely.geometry as geom
from shapely.ops import transform
from pyproj import CRS, Transformer
from math import isclose
# Read input
with open('/work/fiji_transects_wgs84.geojson') as f:
data = json.load(f)
# Fiji Map Grid
fmg_crs = CRS.from_epsg(3460)
wgs84_crs = CRS.from_epsg(4326)
transformer = Transformer.from_crs(wgs84_crs, fmg_crs, always_xy=True)
def normalize_lon(lon):
"""Normalize to -180..180"""
return ((lon + 180) % 360) - 180
def split_at_antimeridian(coords):
"""
Split a list of coordinates into parts that don't cross the antimeridian.
Returns list of lists of coordinates.
"""
parts = []
current_part = [coords[0]]
for i in range(1, len(coords)):
prev_lon, prev_lat = coords[i-1]
cur_lon, cur_lat = coords[i]
# Check if the segment crosses the antimeridian
# (the difference in longitude is > 180 degrees in absolute value)
lon_diff = abs(cur_lon - prev_lon)
if lon_diff > 180:
# It crosses. Find the intersection point at lon=180
# Using linear interpolation
# If prev_lon < cur_lon, we're going west->east (crossing -180), else east->west (crossing 180)
# Determine which direction we're crossing
if prev_lon < 0 and cur_lon > 0: # crossing from west to east (across -180)
# Actually if prev_lon is like -179.9 and cur_lon is 179.9,
# the crossing is at lon=180 (or lon=-180, same point)
# The segment goes from prev_lon to (cur_lon - 360)
adj_cur_lon = cur_lon - 360
# Find t where lon = 180 (but we're going from prev_lon < 0 to adj_cur_lon which is < 0 too... hmm)
# Actually, going from -179.9 to 179.9: the shortest path goes through lon=180
# prev_lon = -179.9, cur_lon = 179.9
# adj_cur_lon = -180.1
# The line goes from -179.9 to -180.1 crossing lon=180... wait that doesn't work either.
# Let's think differently. We're at prev_lon (e.g. -179.9) and want to go to cur_lon (179.9).
# The shortest path goes through lon=180 (going eastward from -179.9 to 180, then to 179.9).
# The crossing point at lon=180: we need to interpolate.
# Going eastward: prev_lon to +180, then wrap to -180 and go to cur_lon.
# Actually cur_lon = 179.9, which is closer to 180 through the positive side.
# So the path is: prev_lon (-179.9) -> 180 -> 179.9
# The crossing point is at lon=180.
pass
elif prev_lon > 0 and cur_lon < 0: # crossing from east to west (across 180)
# e.g. prev_lon=179.9, cur_lon=-179.9
# Going eastward: 179.9 -> 180 -> -179.9... no, eastward from 179.9 goes to 180, then -180, then -179.9
# Actually the shortest path from 179.9 to -179.9 goes through lon=180/-180.
pass
# Let me use a simpler approach
# The crossing is at lon=180 (or lon=-180, same meridian)
# Interpolate the latitude at lon=180
# We need to handle two directions:
# Case 1: prev_lon is positive (~179.x), cur_lon is negative (~-179.x) - crossing 180 going east to west
# Case 2: prev_lon is negative (~-179.x), cur_lon is positive (~179.x) - crossing 180 going west to east
# For Case 1: prev_lon -> +180, then -180 -> cur_lon
# For Case 2: prev_lon -> -180, then +180 -> cur_lon (or equivalently prev_lon -> +180, then -180 -> cur_lon)
# Actually the meridian is the same whether we call it 180 or -180.
# Let me adjust one of the longitudes by +/- 360 to make the segment not wrap
if prev_lon > 0 and cur_lon < 0:
# Going east to west: adjust cur_lon by +360
adj_cur_lon = cur_lon + 360 # e.g., -179.9 + 360 = 180.1
# Now segment goes from prev_lon (~179.9) to adj_cur_lon (~180.1)
# Crossing at lon=180
t = (180 - prev_lon) / (adj_cur_lon - prev_lon)
crossing_lat = prev_lat + t * (cur_lat - prev_lat)
# Part 1: from start to crossing (east side, lon around 180)
# Part 2: from crossing to end (west side, lon around -180)
pass
elif prev_lon < 0 and cur_lon > 0:
# Going west to east: adjust cur_lon by -360
adj_cur_lon = cur_lon - 360 # e.g., 179.9 - 360 = -180.1
# Now segment goes from prev_lon (~-179.9) to adj_cur_lon (~-180.1)
# Crossing at lon=-180 (same as 180)
t = (-180 - prev_lon) / (adj_cur_lon - prev_lon)
crossing_lat = prev_lat + t * (cur_lat - prev_lat)
# Part 1: from start to crossing (west side, lon around -180)
# Part 2: from crossing to end (east side, lon around 180)
pass
# Finish current part with crossing point
crossing_point = (180 if prev_lon > 0 else -180, crossing_lat)
# Actually this is getting complicated. Let me use a different approach.
```
OK, let me think of a cleaner algorithm.
Actually, the simplest approach I know of for handling antimeridian crossing:
1. Start with the line's coordinates.
2. For each consecutive pair, check if the longitudinal gap is > 180 degrees (in absolute value).
3. If so, we need to split. Compute the intersection with the 180° meridian.
Let me think about it more systematically with a concrete example:
T001 coordinates (in order):
[178.94, -17.63], [179.46, -17.62], [179.97, -17.61], [-179.51, -17.60], [-179.00, -17.58], [-178.48, -17.58]
So the transition from [179.97, -17.61] to [-179.51, -17.60] crosses the antimeridian.
Going from 179.97 to -179.51: the shortest path is through 180, going eastward.
- Starting at ~180° (179.97), go to 180°, then wrap to -180°, then to -179.51.
- So the crossing point is at (180, -17.605...).
For the split:
- Part 1: [178.94, -17.63], [179.46, -17.62], [179.97, -17.61], (180, crossing_lat)
- Part 2: (-180, crossing_lat), [-179.51, -17.60], [-179.00, -17.58], [-178.48, -17.58]
But wait, Part 2 starts at -180 which is the same as 180. And the coordinate [-179.51, -17.60] is correct on the west side.
Then when reprojecting to Fiji Map Grid (EPSG:3460), both parts should be fine.
Let me write the code more carefully.
```python
import json
import geopandas as gpd
import numpy as np
from shapely.geometry import LineString, MultiLineString, Point
from shapely.ops import transform
from pyproj import CRS, Transformer
import math
def split_line_at_antimeridian(line_coords):
"""Split a LineString's coordinates at the antimeridian (180°/-180° longitude).
Returns a list of coordinate lists, one for each part."""
parts = []
current_part = [list(line_coords[0])]
for i in range(1, len(line_coords)):
prev = line_coords[i-1]
cur = line_coords[i]
# Check if segment crosses antimeridian (longitude gap > 180 degrees)
if abs(cur[0] - prev[0]) > 180:
# Determine direction
if prev[0] > 0 and cur[0] < 0:
# East to west: segment crosses 180° going east-to-west
# Adjusted cur longitude for interpolation
adj_cur = (cur[0] + 360, cur[1])
# Interpolate where lon=180
t = (180 - prev[0]) / (adj_cur[0] - prev[0])
crossing_lat = prev[1] + t * (cur[1] - prev[1])
# End current part at crossing point (180° side)
current_part.append([180, crossing_lat])
parts.append(current_part)
# Start new part at crossing point (-180° side)
current_part = [[-180, crossing_lat], list(cur)]
else:
# West to east: segment crosses -180° (180°) going west-to-east
adj_cur = (cur[0] - 360, cur[1])
t = (-180 - prev[0]) / (adj_cur[0] - prev[0])
crossing_lat = prev[1] + t * (cur[1] - prev[1])
# End current part at crossing point (-180° side)
current_part.append([-180, crossing_lat])
parts.append(current_part)
# Start new part at crossing point (180° side)
current_part = [[180, crossing_lat], list(cur)]
else:
current_part.append(list(cur))
parts.append(current_part)
return parts
```
This should work! Let me also handle cases where lines extend further east past 180 or west past -180, but for the Fiji data, the coordinates should be in the -180..180 range already.
Actually wait - I need to check the T004 transect. Its coordinates go:
[178.87, -17.80], [179.54, -17.60], [-179.79, -17.41], [-179.12, -17.19], [-178.45, -16.99], [-177.78, -16.80]
So it crosses the antimeridian between [179.54, -17.60] and [-179.79, -17.41]. Going east-to-west.
Let me write the full script now.
For the Fiji Map Grid, I'll use EPSG:3460.
Let me also think about whether I need to handle the case where after splitting, the parts need to be valid geojson with coordinates in the -180..180 range. Yes, the parts I produce have coordinates in the correct range.
After reprojection to FMG, I'll create a MultiLineString if there's more than one part, or a LineString if there's only one (but the instructions say to output MultiLineString for all features).
Actually the instructions say: "Every feature must come out as a MultiLineString, so do not leave any transects as plain LineString". So I'll wrap everything in MultiLineString.
Let me write the script.
1:16
1:16
Write
/work/solve.py
content · 139 lines · py
import json
from shapely.geometry import LineString, MultiLineString, shape
from shapely.ops import transform
from pyproj import CRS, Transformer
import math
# ------------------------------------------------------------
# 1. Read input
# ------------------------------------------------------------
with open('/work/fiji_transects_wgs84.geojson') as f:
raw = json.load(f)
features = raw["features"]
# ------------------------------------------------------------
# 2. CRS set-up
# ------------------------------------------------------------
wgs84 = CRS.from_epsg(4326)
fmg = CRS.from_epsg(3460) # Fiji Map Grid (metres)
tfm = Transformer.from_crs(wgs84, fmg, always_xy=True).transform
# ------------------------------------------------------------
# 3. Helper: split a coordinate list at the antimeridian (180°)
# Returns list of coordinate-lists, one per part.
# ------------------------------------------------------------
def split_at_antimeridian(coords):
"""
coords : list of [lon, lat] pairs (in -180..180).
Returns list of lists-of-pairs, each part not crossing the antimeridian.
"""
parts = []
current = [list(coords[0])]
for i in range(1, len(coords)):
prev = coords[i - 1]
cur = coords[i]
lon_diff = abs(cur[0] - prev[0])
if lon_diff > 180.0:
# ----- segment crosses the antimeridian -----
if prev[0] > 0 and cur[0] < 0:
# East → West: crossing the 180° meridian
# Adjust cur longitude by +360 so segment is continuous
adj_cur = (cur[0] + 360.0, cur[1])
# Interpolate where lon = 180
t = (180.0 - prev[0]) / (adj_cur[0] - prev[0])
xing_lat = prev[1] + t * (cur[1] - prev[1])
# close current part on the +180 side
current.append([180.0, xing_lat])
parts.append(current)
# start new part on the -180 side
current = [[-180.0, xing_lat], list(cur)]
elif prev[0] < 0 and cur[0] > 0:
# West → East: crossing the -180° meridian
adj_cur = (cur[0] - 360.0, cur[1])
t = (-180.0 - prev[0]) / (adj_cur[0] - prev[0])
xing_lat = prev[1] + t * (cur[1] - prev[1])
current.append([-180.0, xing_lat])
parts.append(current)
current = [[180.0, xing_lat], list(cur)]
else:
# Shouldn't happen for normal Fiji data, but be safe
current.append(list(cur))
else:
# No crossing – just continue
current.append(list(cur))
parts.append(current)
return parts
# ------------------------------------------------------------
# 4. Process every feature
# ------------------------------------------------------------
out_features = []
for feat in features:
props = feat["properties"]
geom = shape(feat["geometry"])
coords = list(geom.coords)
# Split at antimeridian if needed
parts_coords = split_at_antimeridian(coords)
# Build individual LineStrings
line_strings = []
for pts in parts_coords:
ls = LineString(pts)
# Reproject to FMG
ls_fmg = transform(tfm, ls)
line_strings.append(ls_fmg)
# Combine into MultiLineString
mls = MultiLineString(line_strings)
# Calculate total length (metres) – sum of part lengths
length_m = sum(ls.length for ls in line_strings)
out_feat = {
"type": "Feature",
"properties": {
"transect_id": props["transect_id"],
"vessel": props["vessel"],
"survey_date": props["survey_date"],
"length_m": round(length_m, 2)
},
"geometry": {
"type": "MultiLineString",
"coordinates": [list(ls.coords) for ls in line_strings]
}
}
out_features.append(out_feat)
# ------------------------------------------------------------
# 5. Write output GeoJSON
# ------------------------------------------------------------
out_collection = {
"type": "FeatureCollection",
"name": "fiji_transects_fmg",
"crs": {
"type": "name",
"properties": {"name": "urn:ogc:def:crs:EPSG::3460"}
},
"features": out_features
}
with open("/work/fiji_transects_fmg.geojson", "w") as f:
json.dump(out_collection, f, indent=2)
print(f"Wrote {len(out_features)} features to /work/fiji_transects_fmg.geojson")
Wrote /work/solve.py (4539 chars).
1:19
Let me run it and check the output.
1:19
Bash
cd /work && python3 solve.py
Wrote 30 features to /work/fiji_transects_fmg.geojson
1:23
Let me verify the output by reading it and checking some key transects.
1:23
Read
/work/fiji_transects_fmg.geojson
1 {
2 "type": "FeatureCollection",
3 "name": "fiji_transects_fmg",
4 "crs": {
5 "type": "name",
6 "properties": {
7 "name": "urn:ogc:def:crs:EPSG::3460"
8 }
9 },
10 "features": [
11 {
12 "type": "Feature",
13 "properties": {
14 "transect_id": "T001",
15 "vessel": "Taveuni II",
16 "survey_date": "2025-08-15",
17 "length_m": 273335.11
18 },
19 "geometry": {
20 "type": "MultiLineString",
21 "coordinates": [
22 [
23 [
24 2020343.340583844,
25 3930017.4674364463
26 ],
27 [
28 2074969.3287187445,
29 3930832.111936373
30 ],
31 [
32 2129608.4030453707,
33 3931959.043532026
34 ],
35 [
36 2132641.3519591475,
37 3932030.6236885176
38 ]
39 ],
40 [
41 [
42 2132641.3519591503,
43 3932030.6236885176
44 ],
45 [
46 2184265.933617849,
47 3933178.8742693216
48 ],
49 [
50 2238946.8792888806,
51 3934438.0861816974
52 ],
53 [
54 2293630.6493575512,
55 3933970.5213900264
56 ]
57 ]
58 ]
59 }
60 },
61 {
62 "type": "Feature",
63 "properties": {
64 "transect_id": "T002",
65 "vessel": "Bligh",
66 "survey_date": "2025-08-12",
67 "length_m": 216575.29
68 },
69 "geometry": {
70 "type": "MultiLineString",
71 "coordinates": [
72 [
73 [
74 2034023.7576998495,
75 3962923.9288114174
76 ],
77 [
78 2103801.3645465984,
79 3980775.0173757505
80 ],
81 [
82 2133006.7821587846,
83 3988176.984284049
84 ]
85 ],
86 [
87 [
88 2133006.7821587874,
89 3988176.984284049
90 ],
91 [
92 2173711.8899896345,
93 3998417.276510603
94 ],
95 [
96 2243773.2911946434,
97 4016860.265945784
98 ]
99 ]
100 ]
101 }
102 },
103 {
104 "type": "Feature",
105 "properties": {
106 "transect_id": "T003",
107 "vessel": "Lomaiviti",
108 "survey_date": "2025-08-12",
109 "length_m": 351823.46
110 },
111 "geometry": {
112 "type": "MultiLineString",
113 "coordinates": [
114 [
115 [
116 1881698.8337329356,
117 3959945.940913004
118 ],
119 [
120 1969475.352082875,
121 3953932.1918599945
122 ],
123 [
124 2057192.4957280513,
125 3947717.5506673623
126 ],
127 [
128 2132707.1824515397,
129 3942028.7593159503
130 ]
131 ],
132 [
133 [
134 2132707.1824515425,
135 3942028.7593159503
136 ],
137 [
138 2144865.828376211,
139 3941086.0440104534
140 ],
141 [
142 2232501.11407936,
143 3933301.4671774227
144 ]
145 ]
146 ]
147 }
148 },
149 {
150 "type": "Feature",
151 "properties": {
152 "transect_id": "T004",
153 "vessel": "Vanua I",
154 "survey_date": "2025-08-15",
155 "length_m": 372666.09
156 },
157 "geometry": {
158 "type": "MultiLineString",
159 "coordinates": [
160 [
161 [
162 2013096.2318831745,
163 3911101.2374744187
164 ],
165 [
166 2084064.7331161185,
167 3933595.078164197
168 ],
169 [
170 2132746.0370150185,
171 3947953.374021069
172 ]
173 ],
174 [
175 [
176 2132746.037015021,
177 3947953.374021069
178 ],
179 [
180 2155191.552825498,
181 3954527.977713378
182 ],
183 [
184 2226505.8923469945,
185 3977271.8304685825
186 ],
187 [
188 2297989.454556443,
189 3998716.763596262
190 ],
191 [
192 2369647.398370236,
193 4019426.6353879017
194 ]
195 ]
196 ]
197 }
198 },
199 {
200 "type": "Feature",
201 "properties": {
202 "transect_id": "T005",
203 "vessel": "Bligh",
204 "survey_date": "2025-08-19",
205 "length_m": 362394.57
206 },
207 "geometry": {
208 "type": "MultiLineString",
209 "coordinates": [
210 [
211 [
212 1877826.2337985218,
213 3897452.1115597077
214 ],
215 [
216 1937585.6830181086,
217 3888095.4565095943
218 ],
219 [
220 1997281.7339928765,
221 3878356.8414042895
222 ],
223 [
224 2056921.5799520519,
225 3869852.4157240363
226 ],
227 [
228 2116501.4382828893,
229 3859064.775548195
230 ],
231 [
232 2132132.72367144,
233 3856392.831893989
234 ]
235 ],
236 [
237 [
238 2132132.723671443,
239 3856392.831893989
240 ],
241 [
242 2176031.798402194,
243 3848816.3934150883
244 ],
245 [
246 2235528.2870951835,
247 3839483.53885849
248 ]
249 ]
250 ]
251 }
252 },
253 {
254 "type": "Feature",
255 "properties": {
256 "transect_id": "T006",
257 "vessel": "Cakaulevu",
258 "survey_date": "2025-08-13",
259 "length_m": 393605.56
260 },
261 "geometry": {
262 "type": "MultiLineString",
263 "coordinates": [
264 [
265 [
266 1986477.0291858702,
267 3847052.749570283
268 ],
269 [
270 2048183.9264241843,
271 3868847.9958887855
272 ],
273 [
274 2110030.3122592983,
275 3889929.569491461
276 ],
277 [
278 2132414.0483812992,
279 3897883.660491682
280 ]
281 ],
282 [
283 [
284 2132414.048381302,
285 3897883.660491682
286 ],
287 [
288 2172028.247827847,
289 3911884.2606283487
290 ],
291 [
292 2234167.3185592326,
293 3932716.7800173583
294 ],
295 [
296 2296469.7202252313,
297 3954296.42636324
298 ],
299 [
300 2358907.291712429,
301 3974351.608875371
302 ]
303 ]
304 ]
305 }
306 },
307 {
308 "type": "Feature",
309 "properties": {
310 "transect_id": "T007",
311 "vessel": "Vanua I",
312 "survey_date": "2025-08-19",
313 "length_m": 414594.38
314 },
315 "geometry": {
316 "type": "MultiLineString",
317 "coordinates": [
318 [
319 [
320 1911692.438271574,
321 3983231.465846738
322 ],
323 [
324 1994362.6345828045,
325 3988871.9466625084
326 ],
327 [
328 2077074.393066105,
329 3993892.378778944
330 ],
331 [
332 2133065.3654978485,
333 3997328.8452844336
334 ]
335 ],
336 [
337 [
338 2133065.3654978513,
339 3997328.8452844336
340 ],
341 [
342 2159841.5664019543,
343 3998918.8506064746
344 ],
345 [
346 2242670.474942513,
347 4003071.442188108
348 ],
349 [
350 2325581.1283138376,
351 4007239.653809817
352 ]
353 ]
354 ]
355 }
356 },
357 {
358 "type": "Feature",
359 "properties": {
360 "transect_id": "T008",
361 "vessel": "Taveuni II",
362 "survey_date": "2025-08-12",
363 "length_m": 146792.55
364 },
365 "geometry": {
366 "type": "MultiLineString",
367 "coordinates": [
368 [
369 [
370 2068958.3651469543,
371 3896474.8093812363
372 ],
373 [
374 2097941.3062179913,
375 3900199.435533137
376 ],
377 [
378 2126942.1228124853,
379 3904630.4103711965
380 ],
381 [
382 2132465.642506932,
383 3905584.342467365
384 ]
385 ],
386 [
387 [
388 2132465.6425069347,
389 3905584.342467365
390 ],
391 [
392 2155963.4499872266,
393 3909624.458919142
394 ],
395 [
396 2184998.036693292,
397 3914066.6121028704
398 ],
399 [
400 2214051.662654211,
401 3918654.097943046
402 ]
403 ]
404 ]
405 }
406 },
407 {
408 "type": "Feature",
409 "properties": {
410 "transect_id": "T009",
411 "vessel": "Vanua I",
412 "survey_date": "2025-08-13",
413 "length_m": 263388.29
414 },
415 "geometry": {
416 "type": "MultiLineString",
417 "coordinates": [
418 [
419 [
420 1927285.4610478787,
421 4037732.1943981266
422 ],
423 [
424 1979888.171983514,
425 4034317.8200324415
426 ],
427 [
428 2032472.1715915422,
429 4030761.5922226436
430 ],
431 [
432 2085042.7925657453,
433 4027610.627861323
434 ],
435 [
436 2133238.856866916,
437 4024688.3579825987
438 ]
439 ],
440 [
441 [
442 2133238.856866919,
443 4024688.3579825987
444 ],
445 [
446 2137604.3518881057,
447 4024418.1879896913
448 ],
449 [
450 2190162.2360857013,
451 4021349.754676055
452 ]
453 ]
454 ]
455 }
456 },
457 {
458 "type": "Feature",
459 "properties": {
460 "transect_id": "T010",
461 "vessel": "Vanua I",
462 "survey_date": "2025-08-15",
463 "length_m": 489842.48
464 },
465 "geometry": {
466 "type": "MultiLineString",
467 "coordinates": [
468 [
469 [
470 1878020.6287486383,
471 3968978.967213469
472 ],
473 [
474 2040133.8855144326,
475 3948137.8948760154
476 ],
477 [
478 2132667.393619207,
479 3935979.8021080676
480 ]
481 ],
482 [
483 [
484 2132667.39361921,
485 3935979.8021080676
486 ],
487 [
488 2201937.698953716,
489 3926588.128825637
490 ],
491 [
492 2363529.5082588177,
493 3904005.3603546056
494 ]
495 ]
496 ]
497 }
498 },
499 {
500 "type": "Feature",
501 "properties": {
502 "transect_id": "T011",
503 "vessel": "Taveuni II",
504 "survey_date": "2025-08-20",
505 "length_m": 78492.17
506 },
507 "geometry": {
508 "type": "MultiLineString",
509 "coordinates": [
510 [
511 [
512 2465344.860581143,
513 3906767.4726070557
514 ],
515 [
516 2452339.7066555577,
517 3904904.8687637383
518 ],
519 [
520 2439358.6785597224,
521 3903920.698017729
522 ],
523 [
524 2426380.6456198604,
525 3902914.5859909956
526 ],
527 [
528 2413382.539078825,
529 3900783.353759157
530 ],
531 [
532 2400405.893514905,
533 3899474.351449523
534 ],
535 [
536 2387423.5467098975,
537 3897686.467113443
538 ]
539 ]
540 ]
541 }
542 },
543 {
544 "type": "Feature",
545 "properties": {
546 "transect_id": "T012",
547 "vessel": "Taveuni II",
548 "survey_date": "2025-08-20",
549 "length_m": 84537.56
550 },
551 "geometry": {
552 "type": "MultiLineString",
553 "coordinates": [
554 [
555 [
556 1741748.7332561535,
557 3890283.693144509
558 ],
559 [
560 1746692.5945271784,
561 3918471.030985262
562 ],
563 [
564 1751658.9788316165,
565 3946432.2638834873
566 ],
567 [
568 1756655.23549125,
569 3973496.030364219
570 ]
571 ]
572 ]
573 }
574 },
575 {
576 "type": "Feature",
577 "properties": {
578 "transect_id": "T013",
579 "vessel": "Bligh",
580 "survey_date": "2025-08-19",
581 "length_m": 179800.48
582 },
583 "geometry": {
584 "type": "MultiLineString",
585 "coordinates": [
586 [
587 [
588 2482137.408760446,
589 3917127.8950326387
590 ],
591 [
592 2428688.657510872,
593 3889083.6235283166
594 ],
595 [
596 2375443.2052126382,
597 3862380.253960575
598 ],
599 [
600 2322350.650514273,
601 3834700.4346561665
602 ]
603 ]
604 ]
605 }
606 },
607 {
608 "type": "Feature",
609 "properties": {
610 "transect_id": "T014",
611 "vessel": "Lomaiviti",
612 "survey_date": "2025-08-13",
613 "length_m": 148392.88
614 },
615 "geometry": {
616 "type": "MultiLineString",
617 "coordinates": [
618 [
619 [
620 1763975.2418630726,
621 4043578.795124353
622 ],
623 [
624 1798590.965242443,
625 4030582.772307513
626 ],
627 [
628 1833155.9390679915,
629 4017897.3350352263
630 ],
631 [
632 1867693.298104288,
633 4002219.6929108296
634 ],
635 [
636 1902160.329611775,
637 3989702.4129796983
638 ]
639 ]
640 ]
641 }
642 },
643 {
644 "type": "Feature",
645 "properties": {
646 "transect_id": "T015",
647 "vessel": "Vanua I",
648 "survey_date": "2025-08-15",
649 "length_m": 124738.16
650 },
651 "geometry": {
652 "type": "MultiLineString",
653 "coordinates": [
654 [
655 [
656 2423739.783700946,
657 4026263.28896241
658 ],
659 [
660 2403163.542824007,
661 4022543.302237784
662 ],
663 [
664 2382622.078231965,
665 4020081.2554188394
666 ],
667 [
668 2362078.3244465566,
669 4016938.6757560875
670 ],
671 [
672 2341534.154948448,
673 4013127.314926124
674 ],
675 [
676 2321005.3259064667,
677 4009569.4162317035
678 ],
679 [
680 2300505.4150438267,
681 4007278.1846613027
682 ]
683 ]
684 ]
685 }
686 },
687 {
688 "type": "Feature",
689 "properties": {
690 "transect_id": "T016",
691 "vessel": "Taveuni II",
692 "survey_date": "2025-08-15",
693 "length_m": 53310.24
694 },
695 "geometry": {
696 "type": "MultiLineString",
697 "coordinates": [
698 [
699 [
700 1967763.1376268503,
701 3864348.9772176393
702 ],
703 [
704 1981044.9340631447,
705 3864774.370362437
706 ],
707 [
708 1994327.5364892795,
709 3864095.7190000876
710 ],
711 [
712 2007608.7888293017,
713 3862426.337582078
714 ],
715 [
716 2020890.884403137,
717 3863623.624927437
718 ]
719 ]
720 ]
721 }
722 },
723 {
724 "type": "Feature",
725 "properties": {
726 "transect_id": "T017",
727 "vessel": "Taveuni II",
728 "survey_date": "2025-08-12",
729 "length_m": 152432.38
730 },
731 "geometry": {
732 "type": "MultiLineString",
733 "coordinates": [
734 [
735 [
736 2459196.783273682,
737 4040076.8577193925
738 ],
739 [
740 2445503.0422038976,
741 4017855.351608406
742 ],
743 [
744 2431865.897774811,
745 3997180.8985599047
746 ],
747 [
748 2418220.5069328533,
749 3974931.1076368974
750 ],
751 [
752 2404624.705595245,
753 3953897.7538502435
754 ],
755 [
756 2391055.945635675,
757 3932981.1069712103
758 ],
759 [
760 2377499.6209094804,
761 3911400.2397073773
762 ]
763 ]
764 ]
765 }
766 },
767 {
768 "type": "Feature",
769 "properties": {
770 "transect_id": "T018",
771 "vessel": "Bligh",
772 "survey_date": "2025-08-19",
773 "length_m": 112421.62
774 },
775 "geometry": {
776 "type": "MultiLineString",
777 "coordinates": [
778 [
779 [
780 1718720.2972681404,
781 4014591.143533892
782 ],
783 [
784 1738197.5781078157,
785 3994553.0214466406
786 ],
787 [
788 1757642.797153981,
789 3974080.649730659
790 ],
791 [
792 1777042.657001174,
793 3954256.1167427967
794 ],
795 [
796 1796415.9225681531,
797 3933347.3340493958
798 ]
799 ]
800 ]
801 }
802 },
803 {
804 "type": "Feature",
805 "properties": {
806 "transect_id": "T019",
807 "vessel": "Lomaiviti",
808 "survey_date": "2025-08-12",
809 "length_m": 179435.73
810 },
811 "geometry": {
812 "type": "MultiLineString",
813 "coordinates": [
814 [
815 [
816 1921232.177273387,
817 3921611.4164991337
818 ],
819 [
820 1877615.3144519601,
821 3932437.2333025597
822 ],
823 [
824 1833949.291065143,
825 3942738.01477121
826 ],
827 [
828 1790234.490579545,
829 3952596.93857858
830 ],
831 [
832 1746468.6202754881,
833 3962251.7484134217
834 ]
835 ]
836 ]
837 }
838 },
839 {
840 "type": "Feature",
841 "properties": {
842 "transect_id": "T020",
843 "vessel": "Taveuni II",
844 "survey_date": "2025-08-13",
845 "length_m": 215352.23
846 },
847 "geometry": {
848 "type": "MultiLineString",
849 "coordinates": [
850 [
851 [
852 1799842.78527887,
853 3989614.6337389858
854 ],
855 [
856 1850089.5464570043,
857 3969622.1670543184
858 ],
859 [
860 1900223.5116513204,
861 3950791.606715858
862 ],
863 [
864 1950261.0912236797,
865 3930816.037580312
866 ],
867 [
868 2000196.12611439,
869 3910677.6427223445
870 ]
871 ]
872 ]
873 }
874 },
875 {
876 "type": "Feature",
877 "properties": {
878 "transect_id": "T021",
879 "vessel": "Vanua I",
880 "survey_date": "2025-08-16",
881 "length_m": 230219.24
882 },
883 "geometry": {
884 "type": "MultiLineString",
885 "coordinates": [
886 [
887 [
888 1734792.6614569672,
889 3988023.0114604924
890 ],
891 [
892 1796140.996215118,
893 3941226.4356850684
894 ],
895 [
896 1857192.2097571124,
897 3895079.338822874
898 ],
899 [
900 1917953.2043430656,
901 3848549.896766379
902 ]
903 ]
904 ]
905 }
906 },
907 {
908 "type": "Feature",
909 "properties": {
910 "transect_id": "T022",
911 "vessel": "Vanua I",
912 "survey_date": "2025-08-15",
913 "length_m": 50981.78
914 },
915 "geometry": {
916 "type": "MultiLineString",
917 "coordinates": [
918 [
919 [
920 1826735.497725808,
921 3908468.0307612177
922 ],
923 [
924 1833530.8357058733,
925 3915690.085356508
926 ],
927 [
928 1840325.6909893686,
929 3923592.9662667327
930 ],
931 [
932 1847131.1924793813,
933 3930812.0080819936
934 ],
935 [
936 1853932.773435727,
937 3939251.123958868
938 ],
939 [
940 1860748.965022611,
941 3946407.5329954834
942 ]
943 ]
944 ]
945 }
946 },
947 {
948 "type": "Feature",
949 "properties": {
950 "transect_id": "T023",
951 "vessel": "Lomaiviti",
952 "survey_date": "2025-08-12",
953 "length_m": 81980.09
954 },
955 "geometry": {
956 "type": "MultiLineString",
957 "coordinates": [
958 [
959 [
960 2430814.5176551496,
961 3920116.8471598867
962 ],
963 [
964 2415479.5967341997,
965 3913735.5872768546
966 ],
967 [
968 2400171.1034651063,
969 3908063.181956481
970 ],
971 [
972 2384901.9803659474,
973 3903846.206101202
974 ],
975 [
976 2369582.6457509208,
977 3896469.9735108716
978 ],
979 [
980 2354312.0603208095,
981 3891057.5105217276
982 ]
983 ]
984 ]
985 }
986 },
987 {
988 "type": "Feature",
989 "properties": {
990 "transect_id": "T024",
991 "vessel": "Vanua I",
992 "survey_date": "2025-08-12",
993 "length_m": 251074.91
994 },
995 "geometry": {
996 "type": "MultiLineString",
997 "coordinates": [
998 [
999 [
1000 2228238.4566725227,
1001 4015411.736894843
1002 ],
1003 [
1004 2276757.963919763,
1005 4002318.5037398003
1006 ],
1007 [
1008 2325229.232446313,
1009 3988882.404731997
1010 ],
1011 [
1012 2373665.340709087,
1013 3975810.063902585
1014 ],
1015 [
1016 2422085.660914305,
1017 3963774.4034233717
1018 ],
1019 [
1020 2470420.227856475,
1021 3949290.9864591043
1022 ]
1023 ]
1024 ]
1025 }
1026 },
1027 {
1028 "type": "Feature",
1029 "properties": {
1030 "transect_id": "T025",
1031 "vessel": "Taveuni II",
1032 "survey_date": "2025-08-19",
1033 "length_m": 108459.05
1034 },
1035 "geometry": {
1036 "type": "MultiLineString",
1037 "coordinates": [
1038 [
1039 [
1040 1769898.113943761,
1041 3863034.971961179
1042 ],
1043 [
1044 1769580.71284751,
1045 3898636.037103303
1046 ],
1047 [
1048 1769258.2869353632,
1049 3935337.3281767596
1050 ],
1051 [
1052 1768950.0859736353,
1053 3971489.878227382
1054 ]
1055 ]
1056 ]
1057 }
1058 },
1059 {
1060 "type": "Feature",
1061 "properties": {
1062 "transect_id": "T026",
1063 "vessel": "Cakaulevu",
1064 "survey_date": "2025-08-19",
1065 "length_m": 100661.48
1066 },
1067 "geometry": {
1068 "type": "MultiLineString",
1069 "coordinates": [
1070 [
1071 [
1072 1987271.4052228667,
1073 3939376.2990370244
1074 ],
1075 [
1076 1967307.9907347183,
1077 3936889.1958410414
1078 ],
1079 [
1080 1947350.861923434,
1081 3933738.235256531
1082 ],
1083 [
1084 1927393.682270505,
1085 3932163.1557427393
1086 ],
1087 [
1088 1907448.9126200052,
1089 3928390.8376744
1090 ],
1091 [
1092 1887498.9116150867,
1093 3926697.0667341426
1094 ]
1095 ]
1096 ]
1097 }
1098 },
1099 {
1100 "type": "Feature",
1101 "properties": {
1102 "transect_id": "T027",
1103 "vessel": "Vanua I",
1104 "survey_date": "2025-08-12",
1105 "length_m": 130089.67
1106 },
1107 "geometry": {
1108 "type": "MultiLineString",
1109 "coordinates": [
1110 [
1111 [
1112 2369948.7263461673,
1113 3976319.9325690367
1114 ],
1115 [
1116 2346287.470686171,
1117 3964968.188987548
1118 ],
1119 [
1120 2322678.80831227,
1121 3955012.8928886973
1122 ],
1123 [
1124 2299080.230015055,
1125 3943891.124994715
1126 ],
1127 [
1128 2275520.6251862263,
1129 3933450.761366028
1130 ],
1131 [
1132 2251971.126515633,
1133 3921591.3665094175
1134 ]
1135 ]
1136 ]
1137 }
1138 },
1139 {
1140 "type": "Feature",
1141 "properties": {
1142 "transect_id": "T028",
1143 "vessel": "Vanua I",
1144 "survey_date": "2025-08-12",
1145 "length_m": 173047.04
1146 },
1147 "geometry": {
1148 "type": "MultiLineString",
1149 "coordinates": [
1150 [
1151 [
1152 2330479.7686202456,
1153 4050623.346325348
1154 ],
1155 [
1156 2372415.289708903,
1157 4039380.3649832667
1158 ],
1159 [
1160 2414329.386504559,
1161 4028381.670683045
1162 ],
1163 [
1164 2456265.7878334904,
1165 4019496.345153804
1166 ],
1167 [
1168 2498125.9280114328,
1169 4007925.0495241787
1170 ]
1171 ]
1172 ]
1173 }
1174 },
1175 {
1176 "type": "Feature",
1177 "properties": {
1178 "transect_id": "T029",
1179 "vessel": "Lomaiviti",
1180 "survey_date": "2025-08-19",
1181 "length_m": 205262.64
1182 },
1183 "geometry": {
1184 "type": "MultiLineString",
1185 "coordinates": [
1186 [
1187 [
1188 2496390.5070293318,
1189 3872926.5081186867
1190 ],
1191 [
1192 2456887.5880763554,
1193 3906639.2715288056
1194 ],
1195 [
1196 2417234.1074046907,
1197 3939224.290901031
1198 ],
1199 [
1200 2377443.041219402,
1201 3970892.0161980125
1202 ],
1203 [
1204 2337541.907860386,
1205 4002897.035326764
1206 ]
1207 ]
1208 ]
1209 }
1210 },
1211 {
1212 "type": "Feature",
1213 "properties": {
1214 "transect_id": "T030",
1215 "vessel": "Cakaulevu",
1216 "survey_date": "2025-08-19",
1217 "length_m": 163829.39
1218 },
1219 "geometry": {
1220 "type": "MultiLineString",
1221 "coordinates": [
1222 [
1223 [
1224 2370599.7528943215,
1225 4007936.284838661
1226 ],
1227 [
1228 2348033.804542877,
1229 3992395.776298311
1230 ],
1231 [
1232 2325521.730368626,
1233 3977972.568507736
1234 ],
1235 [
1236 2303009.840542533,
1237 3961256.978484933
1238 ],
1239 [
1240 2280550.736391237,
1241 3945560.2422843603
1242 ],
1243 [
1244 2258135.8435976296,
1245 3930440.8739923015
1246 ],
1247 [
1248 2235752.355614778,
1249 3914958.146484169
1250 ]
1251 ]
1252 ]
1253 }
1254 }
1255 ]
1256 }
1:54
Let me verify the output looks correct. Let me check a few things: 1. All features are MultiLineString ✓ 2. The antimeridian-crossing features (T001-T010) have been split into multiple parts ✓ 3. The non-antimeridian features (T011-T030) should ideally also be MultiLineString but might show with a single part - let me check that's the case. Wait, looking at the output more carefully, I notice something potentially wrong with the antimeridian handling. Let me look at T001: Original coordinates: [178.94, -17.63], [179.46, -17.62], [179.97, -17.61], [-179.51, -17.60], [-178.99, -17.58], [-178.48, -17.58] The split happens between [179.97, -17.61] and [-179.51, -17.60]. The algorithm: 1. prev=[179.97, -17.61], cur=[-179.51, -17.60] 2. prev[0] > 0 and cur[0] < 0 → East to West crossing 3. adj_cur = (-179.51 + 360, -17.60) = (180.49, -17.60) 4. t = (180 - 179.97) / (180.49 - 179.97) = 0.03 / 0.52 ≈ 0.0577 5. xing_lat = -17.61 + 0.0577 * (-17.60 - (-17.61)) = -17.61 + 0.0577 * 0.01 = -17.6094... So current part 1 gets [..., [180.0, -17.6094]] And part 2 gets [[-180.0, -17.6094], [-179.51, -17.60], ...] Then when reprojecting to FMG: - For part 1: coords are [178.94, -17.63], [179.46, -17.62], [179.97, -17.61], [180.0, -17.6094] → these are all in the "east" side, should project fine - For part 2: coords are [-180.0, -17.6094], [-179.51, -17.60], [-178.99, -17.58], [-178.48, -17.58] → these are all in the "west" side, should project fine Wait, but there's a problem. The reprojection happens using the actual longitude values (-180.0, not 180.0). Let me check what EPSG:3460's area of use is. Fiji 1986 / Fiji Map Grid covers Fiji islands which span from about 177°E to 179°W. So coordinates like 180° or -180° might be at the edge of the projection or problematic. Actually, Fiji Map Grid should handle coordinates in the Fiji area fine. The coordinates at 180° and -180° are both at the antimeridian but the projection should handle them. Let me verify by checking the projection bounds. Actually, looking at the output for T001: Part 1 coordinates (FM reprojected): [2020343, 3930017], [2074969, 3930832], [2129608, 3931959], [2132641, 3932030] Part 2 coordinates (FM reprojected): [2132641, 3932030], [2184265, 3933178], [2238946, 3934438], [2293630, 3933970] The two parts meet at [2132641, 3932030] which is consistent - the insertion point matches. Good. But wait - the issue might be that points at exactly 180.0 or -180.0 might not project correctly with EPSG:3460. Let me verify quickly. Actually, EPSG:3460 (Fiji 1986 / Fiji Map Grid) has this projection: - Projection: Transverse Mercator - Central meridian: 178°E - False easting: 2,000,000 m - False northing: 0 m So a point at lon=180° would be 2° east of the central meridian, which should be fine. But wait, I think there might be a subtle issue. When we split, we insert [180.0, xing_lat] at the end of part 1 and [-180.0, xing_lat] at the start of part 2. But these are the same geographic point - just expressed differently. When projected to FMG, they should come out as the same coordinates (or very close). Let me check T001 output: Part 1 last coord: [2132641.3519591475, 3932030.6236885176] Part 2 first coord: [2132641.3519591503, 3932030.6236885176] Those are very slightly different (1e-12 difference in x) due to floating point. That's fine. Now for the non-antimeridian features - they should still be MultiLineString but with a single part. Let me check T011: Yes, it has a single array of coordinates wrapped in another array. That's correct MultiLineString with one line. Wait, I need to double check something though. For the features that cross the antimeridian, the split_at_antimeridian function adds an extra vertex at 180°/-180°. But the original features already have vertices crossing the antimeridian. Let me trace through T001 more carefully. Original T001 coords (lon, lat): [178.941879580782086, -17.632301816020771] [179.456655009654696, -17.623768593111961] [179.971430438527307, -17.611072392823225] [-179.513794132600054, -17.596198176111951] [-178.999018703727444, -17.579632069513806] [-178.484243274854833, -17.577321872344196] Step 0: i=0, no split, current = [[178.94..., -17.63...]] Step 1: i=1, prev=[178.94...], cur=[179.46...], lon_diff=0.51 < 180, no split. current = [[178.94..., -17.63...], [179.46..., -17.62...]] Step 2: i=2, prev=[179.46...], cur=[179.97...], lon_diff=0.51 < 180, no split. current = [[178.94...], [179.46...], [179.97...]] Step 3: i=3, prev=[179.97...], cur=[-179.51...], lon_diff=359.48 > 180. prev[0] > 0 and cur[0] < 0 → East→West crossing. adj_cur = (-179.51 + 360, -17.60) = (180.49, -17.60) t = (180 - 179.97) / (180.49 - 179.97) = 0.03/0.52 = 0.0577 xing_lat = -17.611 + 0.0577 * (-17.596 - (-17.611)) = -17.611 + 0.0577 * 0.015 = -17.6101 current becomes [[178.94...], [179.46...], [179.97...], [180.0, -17.6101]] parts = [current] current = [[-180.0, -17.6101], [-179.51..., -17.60...]] Step 4: i=4, prev=[-179.51...], cur=[-178.99...], lon_diff=0.51 < 180. current = [[-180.0], [-179.51], [-178.99]] Step 5: i=5, prev=[-178.99...], cur=[-178.48...], lon_diff=0.51 < 180. current = [[-180.0], [-179.51], [-178.99], [-178.48]] End: parts = [part1, part2] This looks correct! The antimeridian features are properly split into two parts. Now let me also check that all features are MultiLineString. Looking at the output, each geometry has type "MultiLineString" with coordinates being an array of arrays. For single-part features (like T011), it's a single array wrapped in an outer array. That's valid GeoJSON MultiLineString. Everything looks good. The output file is at `/work/fiji_transects_fmg.geojson` as requested.
1:54
1:54
Bash
cd /work && python3 -c "
cd /work && python3 -c "
import json
with open('fiji_transects_fmg.geojson') as f:
data = json.load(f)
print(f'Number of features: {len(data[\"features\"])}')
print(f'CRS: {data[\"crs\"]}')
for feat in data['features']:
props = feat['properties']
gtype = feat['geometry']['type']
n_parts = len(feat['geometry']['coordinates'])
tid = props['transect_id']
print(f'{tid}: type={gtype}, parts={n_parts}, length_m={props[\"length_m\"]}, vessel={props[\"vessel\"]}, survey_date={props[\"survey_date\"]}')
"
Number of features: 30
CRS: {'type': 'name', 'properties': {'name': 'urn:ogc:def:crs:EPSG::3460'}}
T001: type=MultiLineString, parts=2, length_m=273335.11, vessel=Taveuni II, survey_date=2025-08-15
T002: type=MultiLineString, parts=2, length_m=216575.29, vessel=Bligh, survey_date=2025-08-12
T003: type=MultiLineString, parts=2, length_m=351823.46, vessel=Lomaiviti, survey_date=2025-08-12
T004: type=MultiLineString, parts=2, length_m=372666.09, vessel=Vanua I, survey_date=2025-08-15
T005: type=MultiLineString, parts=2, length_m=362394.57, vessel=Bligh, survey_date=2025-08-19
T006: type=MultiLineString, parts=2, length_m=393605.56, vessel=Cakaulevu, survey_date=2025-08-13
T007: type=MultiLineString, parts=2, length_m=414594.38, vessel=Vanua I, survey_date=2025-08-19
T008: type=MultiLineString, parts=2, length_m=146792.55, vessel=Taveuni II, survey_date=2025-08-12
T009: type=MultiLineString, parts=2, length_m=263388.29, vessel=Vanua I, survey_date=2025-08-13
T010: type=MultiLineString, parts=2, length_m=489842.48, vessel=Vanua I, survey_date=2025-08-15
T011: type=MultiLineString, parts=1, length_m=78492.17, vessel=Taveuni II, survey_date=2025-08-20
T012: type=MultiLineString, parts=1, length_m=84537.56, vessel=Taveuni II, survey_date=2025-08-20
T013: type=MultiLineString, parts=1, length_m=179800.48, vessel=Bligh, survey_date=2025-08-19
T014: type=MultiLineString, parts=1, length_m=148392.88, vessel=Lomaiviti, survey_date=2025-08-13
T015: type=MultiLineString, parts=1, length_m=124738.16, vessel=Vanua I, survey_date=2025-08-15
T016: type=MultiLineString, parts=1, length_m=53310.24, vessel=Taveuni II, survey_date=2025-08-15
T017: type=MultiLineString, parts=1, length_m=152432.38, vessel=Taveuni II, survey_date=2025-08-12
T018: type=MultiLineString, parts=1, length_m=112421.62, vessel=Bligh, survey_date=2025-08-19
T019: type=MultiLineString, parts=1, length_m=179435.73, vessel=Lomaiviti, survey_date=2025-08-12
T020: type=MultiLineString, parts=1, length_m=215352.23, vessel=Taveuni II, survey_date=2025-08-13
T021: type=MultiLineString, parts=1, length_m=230219.24, vessel=Vanua I, survey_date=2025-08-16
T022: type=MultiLineString, parts=1, length_m=50981.78, vessel=Vanua I, survey_date=2025-08-15
T023: type=MultiLineString, parts=1, length_m=81980.09, vessel=Lomaiviti, survey_date=2025-08-12
T024: type=MultiLineString, parts=1, length_m=251074.91, vessel=Vanua I, survey_date=2025-08-12
T025: type=MultiLineString, parts=1, length_m=108459.05, vessel=Taveuni II, survey_date=2025-08-19
T026: type=MultiLineString, parts=1, length_m=100661.48, vessel=Cakaulevu, survey_date=2025-08-19
T027: type=MultiLineString, parts=1, length_m=130089.67, vessel=Vanua I, survey_date=2025-08-12
T028: type=MultiLineString, parts=1, length_m=173047.04, vessel=Vanua I, survey_date=2025-08-12
T029: type=MultiLineString, parts=1, length_m=205262.64, vessel=Lomaiviti, survey_date=2025-08-19
T030: type=MultiLineString, parts=1, length_m=163829.39, vessel=Cakaulevu, survey_date=2025-08-19
2:01
The results look good. Let me verify that: 1. All 30 features are present 2. All are MultiLineString type 3. The antimeridian-crossing transects (T001-T010) have 2 parts each (split properly) 4. The non-crossing transects (T011-T030) have 1 part each 5. Every feature has transect_id, vessel, survey_date, and length_m 6. The CRS is EPSG:3460 (Fiji Map Grid) Everything checks out. Let me do one final validation to make sure the GeoJSON is valid.
2:01
Bash
cd /work && python3 -c "
cd /work && python3 -c "
import json, sys
with open('fiji_transects_fmg.geojson') as f:
data = json.load(f)
# Basic GeoJSON conformance checks
assert data['type'] == 'FeatureCollection'
for feat in data['features']:
assert feat['type'] == 'Feature'
assert feat['geometry']['type'] == 'MultiLineString'
assert feat['geometry']['coordinates']
props = feat['properties']
for key in ['transect_id', 'vessel', 'survey_date', 'length_m']:
assert key in props
# Check coordinates are 3-4 digit precision EPSG:3460 values (metres)
for part in feat['geometry']['coordinates']:
for coord in part:
assert len(coord) == 2
assert isinstance(coord[0], (int, float))
assert isinstance(coord[1], (int, float))
print('All validations passed!')
"
All validations passed!
2:15
Everything looks good. Let me summarize what was done: 1. **Problem**: The antimeridian-crossing transects (T001-T010) had coordinates crossing the 180° line, which caused rendering engines to draw arcs spanning the entire globe instead of short local segments. 2. **Solution**: - I wrote a `split_at_antimeridian()` function that detects when a segment crosses the 180° meridian (longitude difference > 180°, or sign change) and splits the line at the antimeridian. - When crossing from East to West (e.g., +179° → -179°), it interpolates the crossing point at longitude +180°/-180°, closes the first part, and starts a new part on the other side. - Each part becomes a separate LineString within a MultiLineString. - All features are converted to MultiLineString (even non-crossing ones become single-part MultiLineString). - The geometry is reprojected from WGS84 (EPSG:4326) to Fiji Map Grid (EPSG:3460) in metres. - Each feature gets a `length_m` attribute (sum of part lengths in metres). - Original `transect_id`, `vessel`, and `survey_date` are preserved. 3. **Results**: - 30 features, all MultiLineString type - T001-T010 (antimeridian-crossing) each have 2 parts - T011-T030 (non-crossing) each have 1 part - Lengths computed in metres - CRS set to `urn:ogc:def:crs:EPSG::3460`
2:15