crs-l2-fiji-antimeridian

Status: done
Score: 1.000
Duration: 2:57
Cost: 1.50¢
Model: deepseek/deepseek-v4-flash

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:02
Let me start by reading the input file to understand the data structure and identify the problematic features.
0:02
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:39
Let me analyze the data. The problematic transects are those that cross the antimeridian (180° longitude). When coordinates go from something like +179.97 to -179.51, the line wraps across the entire globe rather than taking the short path across the dateline.

The transects with `crosses_antimeridian_flag: true` are T001 through T010. These have coordinates that jump from positive longitudes (near 180°) to negative longitudes (near -180°) or vice versa, crossing the 180° meridian.

To fix them, I need to:
1. For each feature that crosses the antimeridian, split the LineString into parts where the longitude jumps across the antimeridian
2. Adjust coordinates on one side by adding/subtracting 360° so the segments are local
3. Convert from WGS84 (EPSG:4326) to Fiji's national metric grid (Fiji 1956 / Fiji Map Grid - EPSG:3460? Let me think... Actually, Fiji's national grid is EPSG:3460 - "Fiji 1956 / Fiji Map Grid" or EPSG:3139 - "Fiji 1986 / Fiji Map Grid" or EPSG:3140... Let me check.

Actually, the most common Fiji metric grid is "Fiji 1956 / Fiji Map Grid" which is EPSG:3460. But let me think about what a "national metric grid" for Fiji would be.

Common Fiji CRS:
- EPSG:3460 - Fiji 1956 / Fiji Map Grid (fathoms? No, it's in meters)
- EPSG:3139 - Fiji 1986 / Fiji Map Grid (meters)

Actually, the standard one is EPSG:3460 (Fiji 1956 / Fiji Map Grid). Let me go with EPSG:3460.

Wait, let me think more carefully. Fiji Map Grid uses a Hotine Oblique Mercator projection. EPSG:3460 is "Fiji 1956 / Fiji Map Grid" which uses units of meters. Another option is EPSG:3140 "Fiji 1986 / Fiji Map Grid" also in meters.

Let me just use EPSG:3460 for "Fiji 1956 / Fiji Map Grid" as it's the classic one.

Now, the algorithm to fix antimeridian crossing:
1. Read all coordinates of a LineString
2. Detect where the longitude jumps by more than ~180° in absolute value between consecutive points
3. At each such jump, split into separate LineStrings
4. For coordinates on the "negative" side (west of 180°), add 360° to the longitude to make them continuous
5. This gives us proper multi-part geometries

Let me write a Python script to do this.

The key insight: when a transect goes from, say, (179.97, -17.61) to (-179.51, -17.60), the actual path crosses the antimeridian. The correct representation is to handle the two sides separately.

Actually, thinking more carefully about it. The coordinates are already correct in terms of geographic position. The issue is rendering - when a GIS tries to draw a line from lon=179.97 to lon=-179.51, it goes the long way around (351 degrees) instead of the short way (9 degrees).

To fix this, I need to:
1. Split at each antimeridian crossing
2. For segments on the "east" side (those with positive lon near 180), keep them as is
3. For segments on the "west" side (those with negative lon near -180), add 360 to make them continuous

Actually, let me think about this differently. The proper approach for fixing antimeridian-crossing lines is:

For each consecutive pair of coordinates:
- If the difference in longitude is > 180 (or < -180), the line crosses the antimeridian
- Split at that point, and offset one side by 360°

Let me code this up properly.

Actually, the simplest approach is:
1. Iterate through coordinates
2. When we detect a jump that crosses the antimeridian (abs(delta_lon) > 180), we insert a point at the antimeridian, split the line, and adjust coordinates

But a simpler approach: Since all the antimeridian-crossing transects have this pattern (positive east of 180, negative west of 180), I can:
1. For each transect flagged as crossing, split at the antimeridian crossing point(s)
2. For the western side (negative longitudes), add 360 to make them continuous with the eastern side
3. Create a MultiLineString from the parts

Let me think about the approach more carefully.

Take T001: 
```
[178.94, -17.63], [179.46, -17.62], [179.97, -17.61], [-179.51, -17.60], [-179.00, -17.58], [-178.48, -17.58]
```

The jump is between [179.97, -17.61] and [-179.51, -17.60]. That's a difference of about 359.48 degrees, which crosses the antimeridian.

To fix: split into two lines:
- Line 1 (east side): [178.94, -17.63], [179.46, -17.62], [179.97, -17.61]
- Line 2 (west side, with lon+360): [180.49, -17.60], [181.00, -17.58], [181.52, -17.58]

Wait, -179.51 + 360 = 180.49. So the western segment becomes [180.49, -17.60], [181.00, -17.58], [181.52, -17.58].

But for projection to Fiji grid, these would all be in a continuous space, and after projecting, the lengths would be correct.

Actually, a cleaner approach: I can process all coordinates in one pass.

For each feature:
1. Start with a list of coordinate groups (one group starting with the first point)
2. Iterate through subsequent points
3. If the jump from previous point to current point exceeds 180° in longitude, start a new group
4. For each group, if the group's longitudes are negative (west side), add 360

Let me code this up.

Wait, I also need to handle T010 which goes from east to west AND from west to east:
```
[177.60, -17.28], [179.13, -17.47], [-179.35, -17.65], [-177.82, -17.84]
```

So it starts east of 180, goes to just east, crosses to just west, and goes further west. This is a single crossing, split into 2 parts.

And T004: 
```
[178.87, -17.80], [179.54, -17.60], [-179.79, -17.41], [-179.12, -17.19], [-178.45, -16.99], [-177.78, -16.80]
```
Single crossing, split into 2 parts.

OK, my approach works. Let me code it.

Actually, there's another subtlety. For the metric grid conversion, let me check what Fiji uses.

Let me search for the right EPSG code. Fiji 1956 / Fiji Map Grid is EPSG:3460. But actually, the "Fiji Geographic" CRS (used for the datum) is EPSG:1134? No...

The standard Fiji projected CRS is EPSG:3460 (Fiji 1956 / Fiji Map Grid). Units are metres.

Let me write the script.

```python
import json
import copy
from shapely.geometry import shape, MultiLineString, LineString, mapping
from shapely.ops import transform
import pyproj

# Read input
with open('/work/fiji_transects_wgs84.geojson') as f:
    fc = json.load(f)

# Define CRS
wgs84 = pyproj.CRS('EPSG:4326')
fiji_grid = pyproj.CRS('EPSG:3460')  # Fiji 1956 / Fiji Map Grid

project = pyproj.Transformer.from_crs(wgs84, fiji_grid, always_xy=True).transform

def split_at_antimeridian(coords):
    """Split coordinates at antimeridian crossings.
    Returns list of coordinate lists, each being one part of the line.
    """
    if not coords:
        return []
    
    parts = []
    current_part = [coords[0]]
    
    for i in range(1, len(coords)):
        prev_lon = current_part[-1][0]
        curr_lon = coords[i][0]
        delta = curr_lon - prev_lon
        
        if abs(delta) > 180:
            # Antimeridian crossing - start a new part
            # First, adjust current_part's longitudes if needed
            # We want them all to be in a continuous space
            # If the current_part has negative lons, add 360
            # This will be done after splitting
            
            # Start new part
            parts.append(current_part)
            current_part = [coords[i]]
        else:
            current_part.append(coords[i])
    
    parts.append(current_part)
    
    # Now adjust longitudes: first part keeps original lons
    # Second and later parts: if their lons are negative, add 360
    # Actually, we need to decide which side is which.
    # The first part is the "reference". If the first part has mostly positive lons,
    # then subsequent parts with negative lons get +360.
    # If the first part has negative lons, subsequent parts with positive lons get -360.
    
    if len(parts) > 1:
        # Determine the sign of the first part's longitudes
        first_lons = [p[0] for p in parts[0]]
        first_avg = sum(first_lons) / len(first_lons)
        
        for j in range(1, len(parts)):
            part_lons = [p[0] for p in parts[j]]
            part_avg = sum(part_lons) / len(part_lons)
            
            # If first is positive (east of antimeridian) and this part is negative
            if first_avg > 0 and part_avg < 0:
                parts[j] = [(lon + 360, lat) for lon, lat in parts[j]]
            # If first is negative (west of antimeridian) and this part is positive
            elif first_avg < 0 and part_avg > 0:
                parts[j] = [(lon - 360, lat) for lon, lat in parts[j]]
    
    return parts

out_features = []

for feat in fc['features']:
    props = feat['properties']
    geom = feat['geometry']
    
    if geom['type'] != 'LineString':
        # Already a MultiLineString or other - keep as is but ensure MultiLineString
        # (not expected per input)
        continue
    
    coords = geom['coordinates']
    transect_id = props['transect_id']
    
    # Split at antimeridian if needed
    parts = split_at_antimeridian(coords)
    
    # Create LineStrings
    lines = [LineString(part) for part in parts]
    
    # Create MultiLineString
    if len(lines) == 1:
        # Wrap single LineString in MultiLineString
        multiline = MultiLineString(lines)
    else:
        multiline = MultiLineString(lines)
    
    # Project to Fiji Grid
    projected_multiline = transform(project, multiline)
    
    # Calculate length in meters
    length_m = projected_multiline.length
    
    # Create output feature
    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': mapping(projected_multiline)
    }
    out_features.append(out_feat)

# Write output
out_fc = {
    '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_fc, f, indent=2)

print("Done!")
```

Wait, I need to be more careful with the splitting. Let me re-examine T001:

Coordinates: [178.94, -17.63], [179.46, -17.62], [179.97, -17.61], [-179.51, -17.60], [-179.00, -17.58], [-178.48, -17.58]

delta from [179.97] to [-179.51] = -179.51 - 179.97 = -359.48, abs = 359.48 > 180 → crossing!

So parts would be:
Part 1: [178.94, -17.63], [179.46, -17.62], [179.97, -17.61]
Part 2: [-179.51, -17.60], [-179.00, -17.58], [-178.48, -17.58]

First part avg lon ≈ (178.94+179.46+179.97)/3 = 179.46 (positive)
Second part avg lon ≈ (-179.51-179.00-178.48)/3 = -179.0 (negative)

Since first is positive and second is negative, add 360 to second part:
Part 2 becomes: [180.49, -17.60], [181.00, -17.58], [181.52, -17.58]

Great, that works!

Let me also check T003:
[177.64, -17.36], [178.46, -17.42], [179.29, -17.47], [-179.89, -17.53], [-179.06, -17.59]

delta from [179.29] to [-179.89] = -179.89 - 179.29 = -359.18, abs > 180 → crossing

Part 1: [177.64, -17.36], [178.46, -17.42], [179.29, -17.47]
Part 2: [-179.89, -17.53], [-179.06, -17.59]

Part 1 avg ≈ (177.64+178.46+179.29)/3 = 178.46 (positive)
Part 2 avg ≈ (-179.89-179.06)/2 = -179.475 (negative)
Add 360 to Part 2: [180.11, -17.53], [180.94, -17.59]

Now T010:
[177.60, -17.28], [179.13, -17.47], [-179.35, -17.65], [-177.82, -17.84]

delta from [179.13] to [-179.35] = -179.35 - 179.13 = -358.48, abs > 180 → crossing

Part 1: [177.60, -17.28], [179.13, -17.47]
Part 2: [-179.35, -17.65], [-177.82, -17.84]

Part 1 avg ≈ (177.60+179.13)/2 = 178.37 (positive)
Part 2 avg ≈ (-179.35-177.82)/2 = -178.59 (negative)
Add 360 to Part 2: [180.65, -17.65], [182.18, -17.84]

Good.

Now let me also think about T008:
[179.40, -17.93], [179.67, -17.90], [179.95, -17.86], [-179.78, -17.81], [-179.51, -17.77], [-179.23, -17.72]

delta from [179.95] to [-179.78] = -179.78 - 179.95 = -359.73, abs > 180 → crossing

Part 1: [179.40, -17.93], [179.67, -17.90], [179.95, -17.86]
Part 2: [-179.78, -17.81], [-179.51, -17.77], [-179.23, -17.72]

Part 1 avg ≈ 179.67 (positive)
Part 2 avg ≈ -179.51 (negative)
Add 360: [180.22, -17.81], [180.49, -17.77], [180.77, -17.72]

Great.

Now about the CRS. EPSG:3460 is "Fiji 1956 / Fiji Map Grid". Let me verify it works. The Fiji Map Grid uses the Hotine Oblique Mercator projection and covers all of Fiji. This should work.

Let me write the script.

Actually, I also need to handle the non-antimeridian-crossing transects. For those, I just convert them to MultiLineString (with one part), project, and add length.

Also, I notice T019 has longitudes in order 178.0, 177.6, 177.2, 176.8, 176.4 — going westward but not crossing the antimeridian. This should be fine.

Let me also think about T024:
[-179.11, -16.85], [-178.65, -16.96], [-178.20, -17.08], [-177.74, -17.19], [-177.28, -17.29], [-176.83, -17.41]
No crossing here, all negative longitudes.

OK, let me write the script now.
0:39
0:39
Bash python3 -c "import pyproj; print(pyproj.CRS('EPSG:3460').to_wkt())" 2>&1 | head ...
PROJCRS["Fiji 1986 / Fiji Map Grid",BASEGEOGCRS["Fiji 1986",DATUM["Fiji Geodetic Datum 1986",ELLIPSOID["WGS 72",6378135,298.26,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4720]],CONVERSION["Fiji Map Grid",METHOD["Transverse Mercator",ID["EPSG",9807]],PARAMETER["Latitude of natural origin",-17,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",178.75,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["Scale factor at natural origin",0.99985,SCALEUNIT["unity",1],ID["EPSG",8805]],PARAMETER["False easting",2000000,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",4000000,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["(E)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["(N)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Cadastre, engineering survey, topographic mapping."],AREA["Fiji - onshore. Includes Viti Levu, Vanua Levu, Taveuni, the Yasawa Group, the Kadavu Group, the Lau Islands and Rotuma Islands."],BBOX[-20.81,176.81,-12.42,-178.15]],ID["EPSG",3460]]
1:17
Now I understand the task. Let me analyze:

1. Some transects cross the antimeridian (the 180° meridian). In the GeoJSON, coordinates crossing the antimeridian have longitudes that jump from ~+179 to ~-179 (or vice versa). Since the GeoJSON spec uses simple longitude values without normalization, a line like:
   `[178.94, -17.63], [179.46, -17.62], [179.97, -17.61], [-179.51, -17.60], ...`
   would be interpreted by a naive renderer as going all the way across the globe (from ~180° east to ~180° west = ~360° of longitude), instead of wrapping around the short way through the dateline.

2. To fix this, for transects flagged as crossing the antimeridian, I need to:
   - Split the line at the antimeridian (180° longitude), creating multiple line segments
   - For each segment, adjust longitudes so they are continuous (e.g., add 360° to the negative longitudes on the "east side" of the dateline, or subtract 360° from positive longitudes on the "west side")
   
   Actually, a better approach: I need to think about what "the actual survey path" means. The transects cross the 180° meridian. When you have coordinates like:
   - Points 1-3: ~179°E (east of 180)
   - Points 4-6: ~179°W (west of 180)
   
   The actual path goes from ~179°E across the dateline to ~179°W. But in a simple rendering, the jump from +179.97 to -179.51 is interpreted as going 359.48° westward instead of 0.52° eastward across the dateline.

   The fix is to split the line at the antimeridian. I need to:
   - Find where the longitude jumps across the ±180° boundary
   - Split the line into segments that don't cross the antimeridian
   - For segments east of the dateline, keep longitudes as-is (positive up to 180)
   - For segments west of the dateline, keep longitudes as-is (negative)
   
   But wait - to compute lengths in a projected CRS, I need the coordinates to be "unwrapped" so they're continuous. The standard technique is:
   - When a segment crosses from positive to negative longitude across the dateline, add 360° to the negative longitudes to make them continuous.
   
   Actually, let me think differently. The approach should be:
   
   1. For each transect that crosses the antimeridian, I need to split the LineString into multiple parts at the antimeridian crossing point.
   2. Each part should be a LineString where longitudes don't cross the ±180 boundary.
   3. For the parts east of 180 (longitudes > 0 up to 180), they're fine as-is.
   4. For the parts west of 180 (longitudes < 0, i.e., 180°W to 0°), they're fine as-is.
   
   Then I can project each part to FMG (EPSG:3460), compute lengths, and combine into a MultiLineString.
   
   But wait - the lines crossing the antimeridian, when you look at the coordinates: 
   
   T001: [178.94, ... 179.97, -179.51, ... -178.48]
   
   This goes: 178.94 → 179.46 → 179.97 → -179.51 → -179.00 → -178.48
   
   The actual path across the dateline is from 179.97°E to -179.51°... wait, -179.51 is 179.51°W, which is just 0.52° west of 179.97°E across the dateline. In continuous coordinates, we'd want: 179.97 → 180.49 (or 179.97 → 180.00 → -179.51 → but that doesn't work in simple lon/lat).
   
   Actually for projection purposes, I should first split the line into segments that don't cross the antimeridian, then for each segment, "unwrap" the longitudes so they're continuous for projection.
   
   A common approach:
   1. For the whole set of coordinates, detect where there's a large longitude jump (> 180° difference)
   2. Split at those points
   3. For each resulting segment, make longitudes continuous by adding/subtracting 360° as needed
   4. Project each segment and compute length
   5. Combine into a MultiLineString

   Actually, let me think about this more carefully. The classic "antimeridian splitting" approach:
   
   For T001: 
   Points: (178.94, -17.63), (179.46, -17.62), (179.97, -17.61), (-179.51, -17.60), (-179.00, -17.58), (-178.48, -17.58)
   
   The jump between point 3 (179.97°E) and point 4 (-179.51°W) is 179.97 - (-179.51) = 359.48° in raw coordinates. The actual difference across the dateline is 360 - 359.48 = 0.52°.
   
   To make this continuous for projection, I can shift the negative longitude points by +360°:
   Segment 1 (east of dateline): (178.94, -17.63), (179.46, -17.62), (179.97, -17.61)
   Segment 2 (west of dateline, adjusted +360°): (180.49, -17.60), (181.00, -17.58), (181.52, -17.58)
   
   But wait, the actual geographic coordinates of point 4 are at -179.51° (179.51°W). If I add 360°, it becomes 180.49° which is 179.51°W... that's correct in terms of position but the longitude is > 180.
   
   Actually for projection, pyproj should handle longitudes > 180 (or < -180) as long as they represent continuous positions. Let me check - EPSG:3460 is a Transverse Mercator centered at 178.75°E, so it should handle coordinates wrapping around.
   
   Let me think about this differently. The simplest approach:
   
   1. For lines that cross the antimeridian, I need to make the coordinates "continuous" by shifting one side by ±360°
   2. Project the continuous version to FMG
   3. Split the projected line into segments at the antimeridian crossing
   4. Each segment becomes a part of a MultiLineString
   5. The total length is the sum of all parts' lengths
   
   Actually, let me use a simpler, more robust approach with shapely's `intersection` with a bounding box. Or better yet, use the approach of splitting at the antimeridian.
   
   Let me use the `normalize` approach from shapely or a custom splitter.

   Here's my plan:
   1. Read the GeoJSON
   2. For each feature:
      a. If it's flagged as crossing the antimeridian (or I detect a large longitude jump), split the LineString at the antimeridian
      b. For each part, project to EPSG:3460 (Fiji Map Grid)
      c. Compute the length of each part in metres
      d. Combine parts into a MultiLineString in WGS84
      e. Add the total length_m attribute
   3. Write the result as GeoJSON

   For splitting at the antimeridian, I'll use this approach:
   - Iterate through coordinate pairs
   - If consecutive longitudes have a difference > 180° in absolute value, split there
   - At the split point, interpolate to find where the line crosses 180° longitude (or -180°)
   
   Let me implement this step by step.

   Actually, a simpler approach: use `shapely.ops.split` with a line at the antimeridian. Or I can use the `normalize` method.

   Let me think about using a cleaner approach:
   
   For each feature:
   1. Check if it crosses the antimeridian by looking for absolute longitude differences > 180° between consecutive points.
   2. If it does cross, split into segments at the crossing points.
   3. For each segment, adjust longitudes so they're in a consistent range (no wrapping).
   4. Convert to Fiji Map Grid and measure length.
   5. Convert back to WGS84 for output (keeping original ranges).
   6. Assemble as MultiLineString.

   Wait, the output should be in FMG coordinates ("convert them to Fiji's national metric grid"). So the output GeoJSON should have projected coordinates! Let me re-read:

   "convert them to Fiji's national metric grid, and give me each transect as one MultiLineString feature with a length_m attribute in metres"
   
   So the output is in EPSG:3460 (Fiji Map Grid) projected CRS. The geometry has FMG coordinates and length_m is in metres.

   "write the result as GeoJSON named fiji_transects_fmg.geojson"

   For the GeoJSON, I need to include the CRS definition.

   Let me write the Python script.

   For splitting at the antimeridian, here's a clean approach:

   For T001: coordinates are [178.94, ... 179.97, -179.51, ... -178.48]
   
   I'll:
   1. Detect where the jump > 180° is (between index 2 and 3: 179.97 to -179.51, diff = 359.48)
   2. Interpolate the crossing point at lon=180° (or lon=-180°)
   3. Create segment 1: from start to the crossing point (all on east side, longitudes > 0)
   4. Create segment 2: from crossing point to end (all on west side, longitudes < 0)
   
   Actually, the crossing happens at 180°, so between 179.97°E and 179.51°W. Going from east to west, we cross 180° longitude (the dateline from east to west).
   
   Let me interpolate: at lon=179.97 going to lon=-179.51 (which is 179.51°W = 180.49° in the other direction), the line crosses 180°E (which is also -180°W).
   
   In terms of "continuous" longitude: 
   - point 3: 179.97
   - point 4: 179.51°W = -179.51° (but in continuous space going eastward, it would be 180.49°)
   
   The interpolation parameter t where lon=180: 
   t = (180 - 179.97) / (180.49 - 179.97) = 0.03 / 0.52 ≈ 0.0577
   
   At that point, lat ≈ -17.61 + 0.0577 * (-17.60 - (-17.61)) = -17.61 + 0.0577 * 0.01 ≈ -17.6094
   
   This gives the crossing point at (180°, -17.6094°).

   OK let me just code this up systematically.

   For the approach, I'll:
   1. Detect antimeridian crossings (|lon_diff| > 180° between consecutive points)
   2. Split the line at each crossing by interpolating the intersection with the antimeridian
   3. Build segments that don't cross
   4. Convert each segment to FMG, compute length
   5. Output as MultiLineString in FMG coordinates with length_m

   Let me write the script now.
1:17
Write /work/solve.py
content · 193 lines · py
#!/usr/bin/env python3
"""
Fix antimeridian-crossing transects, project to Fiji Map Grid (EPSG:3460),
and compute per-transect length in metres.
Output: fiji_transects_fmg.geojson (MultiLineString features in FMG CRS).
"""

import json
import math
import sys

import pyproj
from shapely.geometry import (
    LineString,
    MultiLineString,
    shape,
    mapping,
)
from shapely.ops import transform

# ── CRS ──────────────────────────────────────────────────────────────────
WGS84 = pyproj.CRS("EPSG:4326")
FMG   = pyproj.CRS("EPSG:3460")   # Fiji 1986 / Fiji Map Grid
transformer = pyproj.Transformer.from_crs(WGS84, FMG, always_xy=True)

# ── Coordinates of the antimeridian ──────────────────────────────────────
MERIDIAN = 180.0   # ±180°

def _interpolate_antimeridian(p1, p2):
    """Return the (lon, lat) where the great-circle arc p1→p2 crosses 180°E/W.

    p1 and p2 are (lon, lat) in degrees.  If p1 and p2 straddle the
    antimeridian (i.e. their longitudes differ by >180°) then linearly
    interpolate the latitude at lon=180° in the *continuous* domain.
    """
    lon1, lat1 = p1
    lon2, lat2 = p2

    # Make continuous: shift the far-side longitude by ±360 so there is
    # no wrap.
    if lon2 - lon1 > 180:
        lon2 -= 360
    elif lon1 - lon2 > 180:
        lon2 += 360

    # Avoid division by zero
    if abs(lon2 - lon1) < 1e-12:
        return (MERIDIAN, (lat1 + lat2) / 2.0)

    t = (MERIDIAN - lon1) / (lon2 - lon1)
    # t should be in [0, 1] if the segment really crosses.
    lat = lat1 + t * (lat2 - lat1)
    # Normalise longitude back to [-180, 180)
    lon = MERIDIAN
    return (lon, lat)


def split_at_antimeridian(coords):
    """Split a list of (lon, lat) into sub-lists that do *not* cross ±180°.

    Returns a list of lists-of-coordinates.  Adjacent segments share the
    crossing point on the meridian as first/last vertex.
    """
    segments = []
    current = [coords[0]]

    for i in range(1, len(coords)):
        prev = coords[i - 1]
        cur = coords[i]

        lon_diff = cur[0] - prev[0]
        # Normalise to [-180, 180] for the difference check
        if lon_diff > 180:
            lon_diff -= 360
        elif lon_diff < -180:
            lon_diff += 360

        if abs(lon_diff) > 170:   # crosses the antimeridian
            cross_pt = _interpolate_antimeridian(prev, cur)
            # Close the current segment at the crossing point
            current.append(cross_pt)
            segments.append(current)
            # Start a new segment from the crossing point
            current = [cross_pt, cur]
        else:
            current.append(cur)

    segments.append(current)
    return segments


def line_length_fmg(coords):
    """Project a list of WGS84 (lon,lat) to FMG and return length in metres."""
    if len(coords) < 2:
        return 0.0
    line = LineString(coords)
    line_fmg = transform(transformer.transform, line)
    return line_fmg.length


def build_multiline_fmg(segments_coords):
    """Convert each list of WGS84 coords to a projected FMG LineString,
    return a MultiLineString in FMG CRS."""
    parts = []
    for seg in segments_coords:
        if len(seg) >= 2:
            line = LineString(seg)
            line_fmg = transform(transformer.transform, line)
            parts.append(line_fmg)
    if not parts:
        return None
    return MultiLineString(parts)


# ── Main ─────────────────────────────────────────────────────────────────
def main():
    with open("/work/fiji_transects_wgs84.geojson", "r") as f:
        fc = json.load(f)

    out_features = []

    for feat in fc["features"]:
        props = feat["properties"]
        geom = shape(feat["geometry"])

        if geom.geom_type not in ("LineString", "MultiLineString"):
            continue

        transect_id = props.get("transect_id", "???")

        # Collect all coordinates (handle both LineString and MultiLineString input)
        if geom.geom_type == "LineString":
            all_coords = list(geom.coords)
        else:  # MultiLineString
            all_coords = []
            for part in geom.geoms:
                all_coords.extend(list(part.coords))

        # Detect if this line crosses the antimeridian
        crosses = False
        for i in range(1, len(all_coords)):
            lon_diff = all_coords[i][0] - all_coords[i-1][0]
            if lon_diff > 180:
                lon_diff -= 360
            elif lon_diff < -180:
                lon_diff += 360
            if abs(lon_diff) > 170:
                crosses = True
                break

        if crosses:
            segments = split_at_antimeridian(all_coords)
        else:
            segments = [all_coords]

        # Build the projected MultiLineString
        ml_fmg = build_multiline_fmg(segments)
        if ml_fmg is None:
            continue

        total_length = sum(part.length for part in ml_fmg.geoms)

        out_feat = {
            "type": "Feature",
            "properties": {
                "transect_id": transect_id,
                "vessel": props.get("vessel"),
                "survey_date": props.get("survey_date"),
                "length_m": round(total_length, 2),
            },
            "geometry": mapping(ml_fmg),
        }
        out_features.append(out_feat)

    # Write output GeoJSON with explicit CRS
    out_fc = {
        "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_fc, f, indent=2)

    print(f"Wrote {len(out_features)} features to fiji_transects_fmg.geojson")


if __name__ == "__main__":
    main()
Wrote /work/solve.py (5805 chars).
1:20
Let me run the script first to see if it works.
1:20
Bash cd /work && python solve.py
Wrote 30 features to fiji_transects_fmg.geojson
1:25
Good, it ran successfully. Let me verify the output to make sure everything looks correct.

Key checks:
1. Every feature must be a MultiLineString
2. The antimeridian-crossing ones (T001-T010) should have multiple parts
3. The non-crossing ones should have a single-part MultiLineString
4. All features carry vessel and survey_date
5. length_m in metres
6. CRS is EPSG:3460 (Fiji Map Grid)
7. transect_id is the key field

Let me check the output.
1:25
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	              2184265.933617849,
    37	              3933178.8742693216
    38	            ],
    39	            [
    40	              2238946.8792888806,
    41	              3934438.0861816974
    42	            ],
    43	            [
    44	              2293630.6493575512,
    45	              3933970.5213900264
    46	            ]
    47	          ]
    48	        ]
    49	      }
    50	    },
    51	    {
    52	      "type": "Feature",
    53	      "properties": {
    54	        "transect_id": "T002",
    55	        "vessel": "Bligh",
    56	        "survey_date": "2025-08-12",
    57	        "length_m": 216575.27
    58	      },
    59	      "geometry": {
    60	        "type": "MultiLineString",
    61	        "coordinates": [
    62	          [
    63	            [
    64	              2034023.7576998495,
    65	              3962923.9288114174
    66	            ],
    67	            [
    68	              2103801.3645465984,
    69	              3980775.0173757505
    70	            ],
    71	            [
    72	              2173711.8899896345,
    73	              3998417.276510603
    74	            ],
    75	            [
    76	              2243773.2911946434,
    77	              4016860.265945784
    78	            ]
    79	          ]
    80	        ]
    81	      }
    82	    },
    83	    {
    84	      "type": "Feature",
    85	      "properties": {
    86	        "transect_id": "T003",
    87	        "vessel": "Lomaiviti",
    88	        "survey_date": "2025-08-12",
    89	        "length_m": 351823.43
    90	      },
    91	      "geometry": {
    92	        "type": "MultiLineString",
    93	        "coordinates": [
    94	          [
    95	            [
    96	              1881698.8337329356,
    97	              3959945.940913004
    98	            ],
    99	            [
   100	              1969475.352082875,
   101	              3953932.1918599945
   102	            ],
   103	            [
   104	              2057192.4957280513,
   105	              3947717.5506673623
   106	            ],
   107	            [
   108	              2144865.828376211,
   109	              3941086.0440104534
   110	            ],
   111	            [
   112	              2232501.11407936,
   113	              3933301.4671774227
   114	            ]
   115	          ]
   116	        ]
   117	      }
   118	    },
   119	    {
   120	      "type": "Feature",
   121	      "properties": {
   122	        "transect_id": "T004",
   123	        "vessel": "Vanua I",
   124	        "survey_date": "2025-08-15",
   125	        "length_m": 372666.07
   126	      },
   127	      "geometry": {
   128	        "type": "MultiLineString",
   129	        "coordinates": [
   130	          [
   131	            [
   132	              2013096.2318831745,
   133	              3911101.2374744187
   134	            ],
   135	            [
   136	              2084064.7331161185,
   137	              3933595.078164197
   138	            ],
   139	            [
   140	              2155191.552825498,
   141	              3954527.977713378
   142	            ],
   143	            [
   144	              2226505.8923469945,
   145	              3977271.8304685825
   146	            ],
   147	            [
   148	              2297989.454556443,
   149	              3998716.763596262
   150	            ],
   151	            [
   152	              2369647.398370236,
   153	              4019426.6353879017
   154	            ]
   155	          ]
   156	        ]
   157	      }
   158	    },
   159	    {
   160	      "type": "Feature",
   161	      "properties": {
   162	        "transect_id": "T005",
   163	        "vessel": "Bligh",
   164	        "survey_date": "2025-08-19",
   165	        "length_m": 362394.55
   166	      },
   167	      "geometry": {
   168	        "type": "MultiLineString",
   169	        "coordinates": [
   170	          [
   171	            [
   172	              1877826.2337985218,
   173	              3897452.1115597077
   174	            ],
   175	            [
   176	              1937585.6830181086,
   177	              3888095.4565095943
   178	            ],
   179	            [
   180	              1997281.7339928765,
   181	              3878356.8414042895
   182	            ],
   183	            [
   184	              2056921.5799520519,
   185	              3869852.4157240363
   186	            ],
   187	            [
   188	              2116501.4382828893,
   189	              3859064.775548195
   190	            ],
   191	            [
   192	              2176031.798402194,
   193	              3848816.3934150883
   194	            ],
   195	            [
   196	              2235528.2870951835,
   197	              3839483.53885849
   198	            ]
   199	          ]
   200	        ]
   201	      }
   202	    },
   203	    {
   204	      "type": "Feature",
   205	      "properties": {
   206	        "transect_id": "T006",
   207	        "vessel": "Cakaulevu",
   208	        "survey_date": "2025-08-13",
   209	        "length_m": 393605.54
   210	      },
   211	      "geometry": {
   212	        "type": "MultiLineString",
   213	        "coordinates": [
   214	          [
   215	            [
   216	              1986477.0291858702,
   217	              3847052.749570283
   218	            ],
   219	            [
   220	              2048183.9264241843,
   221	              3868847.9958887855
   222	            ],
   223	            [
   224	              2110030.3122592983,
   225	              3889929.569491461
   226	            ],
   227	            [
   228	              2172028.247827847,
   229	              3911884.2606283487
   230	            ],
   231	            [
   232	              2234167.3185592326,
   233	              3932716.7800173583
   234	            ],
   235	            [
   236	              2296469.7202252313,
   237	              3954296.42636324
   238	            ],
   239	            [
   240	              2358907.291712429,
   241	              3974351.608875371
   242	            ]
   243	          ]
   244	        ]
   245	      }
   246	    },
   247	    {
   248	      "type": "Feature",
   249	      "properties": {
   250	        "transect_id": "T007",
   251	        "vessel": "Vanua I",
   252	        "survey_date": "2025-08-19",
   253	        "length_m": 414594.34
   254	      },
   255	      "geometry": {
   256	        "type": "MultiLineString",
   257	        "coordinates": [
   258	          [
   259	            [
   260	              1911692.438271574,
   261	              3983231.465846738
   262	            ],
   263	            [
   264	              1994362.6345828045,
   265	              3988871.9466625084
   266	            ],
   267	            [
   268	              2077074.393066105,
   269	              3993892.378778944
   270	            ],
   271	            [
   272	              2159841.5664019543,
   273	              3998918.8506064746
   274	            ],
   275	            [
   276	              2242670.474942513,
   277	              4003071.442188108
   278	            ],
   279	            [
   280	              2325581.1283138376,
   281	              4007239.653809817
   282	            ]
   283	          ]
   284	        ]
   285	      }
   286	    },
   287	    {
   288	      "type": "Feature",
   289	      "properties": {
   290	        "transect_id": "T008",
   291	        "vessel": "Taveuni II",
   292	        "survey_date": "2025-08-12",
   293	        "length_m": 146792.54
   294	      },
   295	      "geometry": {
   296	        "type": "MultiLineString",
   297	        "coordinates": [
   298	          [
   299	            [
   300	              2068958.3651469543,
   301	              3896474.8093812363
   302	            ],
   303	            [
   304	              2097941.3062179913,
   305	              3900199.435533137
   306	            ],
   307	            [
   308	              2126942.1228124853,
   309	              3904630.4103711965
   310	            ],
   311	            [
   312	              2155963.4499872266,
   313	              3909624.458919142
   314	            ],
   315	            [
   316	              2184998.036693292,
   317	              3914066.6121028704
   318	            ],
   319	            [
   320	              2214051.662654211,
   321	              3918654.097943046
   322	            ]
   323	          ]
   324	        ]
   325	      }
   326	    },
   327	    {
   328	      "type": "Feature",
   329	      "properties": {
   330	        "transect_id": "T009",
   331	        "vessel": "Vanua I",
   332	        "survey_date": "2025-08-13",
   333	        "length_m": 263388.29
   334	      },
   335	      "geometry": {
   336	        "type": "MultiLineString",
   337	        "coordinates": [
   338	          [
   339	            [
   340	              1927285.4610478787,
   341	              4037732.1943981266
   342	            ],
   343	            [
   344	              1979888.171983514,
   345	              4034317.8200324415
   346	            ],
   347	            [
   348	              2032472.1715915422,
   349	              4030761.5922226436
   350	            ],
   351	            [
   352	              2085042.7925657453,
   353	              4027610.627861323
   354	            ],
   355	            [
   356	              2137604.3518881057,
   357	              4024418.1879896913
   358	            ],
   359	            [
   360	              2190162.2360857013,
   361	              4021349.754676055
   362	            ]
   363	          ]
   364	        ]
   365	      }
   366	    },
   367	    {
   368	      "type": "Feature",
   369	      "properties": {
   370	        "transect_id": "T010",
   371	        "vessel": "Vanua I",
   372	        "survey_date": "2025-08-15",
   373	        "length_m": 489842.14
   374	      },
   375	      "geometry": {
   376	        "type": "MultiLineString",
   377	        "coordinates": [
   378	          [
   379	            [
   380	              1878020.6287486383,
   381	              3968978.967213469
   382	            ],
   383	            [
   384	              2040133.8855144326,
   385	              3948137.8948760154
   386	            ],
   387	            [
   388	              2201937.698953716,
   389	              3926588.128825637
   390	            ],
   391	            [
   392	              2363529.5082588177,
   393	              3904005.3603546056
   394	            ]
   395	          ]
   396	        ]
   397	      }
   398	    },
   399	    {
   400	      "type": "Feature",
   401	      "properties": {
   402	        "transect_id": "T011",
   403	        "vessel": "Taveuni II",
   404	        "survey_date": "2025-08-20",
   405	        "length_m": 78492.17
   406	      },
   407	      "geometry": {
   408	        "type": "MultiLineString",
   409	        "coordinates": [
   410	          [
   411	            [
   412	              2465344.860581143,
   413	              3906767.4726070557
   414	            ],
   415	            [
   416	              2452339.7066555577,
   417	              3904904.8687637383
   418	            ],
   419	            [
   420	              2439358.6785597224,
   421	              3903920.698017729
   422	            ],
   423	            [
   424	              2426380.6456198604,
   425	              3902914.5859909956
   426	            ],
   427	            [
   428	              2413382.539078825,
   429	              3900783.353759157
   430	            ],
   431	            [
   432	              2400405.893514905,
   433	              3899474.351449523
   434	            ],
   435	            [
   436	              2387423.5467098975,
   437	              3897686.467113443
   438	            ]
   439	          ]
   440	        ]
   441	      }
   442	    },
   443	    {
   444	      "type": "Feature",
   445	      "properties": {
   446	        "transect_id": "T012",
   447	        "vessel": "Taveuni II",
   448	        "survey_date": "2025-08-20",
   449	        "length_m": 84537.56
   450	      },
   451	      "geometry": {
   452	        "type": "MultiLineString",
   453	        "coordinates": [
   454	          [
   455	            [
   456	              1741748.7332561535,
   457	              3890283.693144509
   458	            ],
   459	            [
   460	              1746692.5945271784,
   461	              3918471.030985262
   462	            ],
   463	            [
   464	              1751658.9788316165,
   465	              3946432.2638834873
   466	            ],
   467	            [
   468	              1756655.23549125,
   469	              3973496.030364219
   470	            ]
   471	          ]
   472	        ]
   473	      }
   474	    },
   475	    {
   476	      "type": "Feature",
   477	      "properties": {
   478	        "transect_id": "T013",
   479	        "vessel": "Bligh",
   480	        "survey_date": "2025-08-19",
   481	        "length_m": 179800.48
   482	      },
   483	      "geometry": {
   484	        "type": "MultiLineString",
   485	        "coordinates": [
   486	          [
   487	            [
   488	              2482137.408760446,
   489	              3917127.8950326387
   490	            ],
   491	            [
   492	              2428688.657510872,
   493	              3889083.6235283166
   494	            ],
   495	            [
   496	              2375443.2052126382,
   497	              3862380.253960575
   498	            ],
   499	            [
   500	              2322350.650514273,
   501	              3834700.4346561665
   502	            ]
   503	          ]
   504	        ]
   505	      }
   506	    },
   507	    {
   508	      "type": "Feature",
   509	      "properties": {
   510	        "transect_id": "T014",
   511	        "vessel": "Lomaiviti",
   512	        "survey_date": "2025-08-13",
   513	        "length_m": 148392.88
   514	      },
   515	      "geometry": {
   516	        "type": "MultiLineString",
   517	        "coordinates": [
   518	          [
   519	            [
   520	              1763975.2418630726,
   521	              4043578.795124353
   522	            ],
   523	            [
   524	              1798590.965242443,
   525	              4030582.772307513
   526	            ],
   527	            [
   528	              1833155.9390679915,
   529	              4017897.3350352263
   530	            ],
   531	            [
   532	              1867693.298104288,
   533	              4002219.6929108296
   534	            ],
   535	            [
   536	              1902160.329611775,
   537	              3989702.4129796983
   538	            ]
   539	          ]
   540	        ]
   541	      }
   542	    },
   543	    {
   544	      "type": "Feature",
   545	      "properties": {
   546	        "transect_id": "T015",
   547	        "vessel": "Vanua I",
   548	        "survey_date": "2025-08-15",
   549	        "length_m": 124738.16
   550	      },
   551	      "geometry": {
   552	        "type": "MultiLineString",
   553	        "coordinates": [
   554	          [
   555	            [
   556	              2423739.783700946,
   557	              4026263.28896241
   558	            ],
   559	            [
   560	              2403163.542824007,
   561	              4022543.302237784
   562	            ],
   563	            [
   564	              2382622.078231965,
   565	              4020081.2554188394
   566	            ],
   567	            [
   568	              2362078.3244465566,
   569	              4016938.6757560875
   570	            ],
   571	            [
   572	              2341534.154948448,
   573	              4013127.314926124
   574	            ],
   575	            [
   576	              2321005.3259064667,
   577	              4009569.4162317035
   578	            ],
   579	            [
   580	              2300505.4150438267,
   581	              4007278.1846613027
   582	            ]
   583	          ]
   584	        ]
   585	      }
   586	    },
   587	    {
   588	      "type": "Feature",
   589	      "properties": {
   590	        "transect_id": "T016",
   591	        "vessel": "Taveuni II",
   592	        "survey_date": "2025-08-15",
   593	        "length_m": 53310.24
   594	      },
   595	      "geometry": {
   596	        "type": "MultiLineString",
   597	        "coordinates": [
   598	          [
   599	            [
   600	              1967763.1376268503,
   601	              3864348.9772176393
   602	            ],
   603	            [
   604	              1981044.9340631447,
   605	              3864774.370362437
   606	            ],
   607	            [
   608	              1994327.5364892795,
   609	              3864095.7190000876
   610	            ],
   611	            [
   612	              2007608.7888293017,
   613	              3862426.337582078
   614	            ],
   615	            [
   616	              2020890.884403137,
   617	              3863623.624927437
   618	            ]
   619	          ]
   620	        ]
   621	      }
   622	    },
   623	    {
   624	      "type": "Feature",
   625	      "properties": {
   626	        "transect_id": "T017",
   627	        "vessel": "Taveuni II",
   628	        "survey_date": "2025-08-12",
   629	        "length_m": 152432.38
   630	      },
   631	      "geometry": {
   632	        "type": "MultiLineString",
   633	        "coordinates": [
   634	          [
   635	            [
   636	              2459196.783273682,
   637	              4040076.8577193925
   638	            ],
   639	            [
   640	              2445503.0422038976,
   641	              4017855.351608406
   642	            ],
   643	            [
   644	              2431865.897774811,
   645	              3997180.8985599047
   646	            ],
   647	            [
   648	              2418220.5069328533,
   649	              3974931.1076368974
   650	            ],
   651	            [
   652	              2404624.705595245,
   653	              3953897.7538502435
   654	            ],
   655	            [
   656	              2391055.945635675,
   657	              3932981.1069712103
   658	            ],
   659	            [
   660	              2377499.6209094804,
   661	              3911400.2397073773
   662	            ]
   663	          ]
   664	        ]
   665	      }
   666	    },
   667	    {
   668	      "type": "Feature",
   669	      "properties": {
   670	        "transect_id": "T018",
   671	        "vessel": "Bligh",
   672	        "survey_date": "2025-08-19",
   673	        "length_m": 112421.62
   674	      },
   675	      "geometry": {
   676	        "type": "MultiLineString",
   677	        "coordinates": [
   678	          [
   679	            [
   680	              1718720.2972681404,
   681	              4014591.143533892
   682	            ],
   683	            [
   684	              1738197.5781078157,
   685	              3994553.0214466406
   686	            ],
   687	            [
   688	              1757642.797153981,
   689	              3974080.649730659
   690	            ],
   691	            [
   692	              1777042.657001174,
   693	              3954256.1167427967
   694	            ],
   695	            [
   696	              1796415.9225681531,
   697	              3933347.3340493958
   698	            ]
   699	          ]
   700	        ]
   701	      }
   702	    },
   703	    {
   704	      "type": "Feature",
   705	      "properties": {
   706	        "transect_id": "T019",
   707	        "vessel": "Lomaiviti",
   708	        "survey_date": "2025-08-12",
   709	        "length_m": 179435.73
   710	      },
   711	      "geometry": {
   712	        "type": "MultiLineString",
   713	        "coordinates": [
   714	          [
   715	            [
   716	              1921232.177273387,
   717	              3921611.4164991337
   718	            ],
   719	            [
   720	              1877615.3144519601,
   721	              3932437.2333025597
   722	            ],
   723	            [
   724	              1833949.291065143,
   725	              3942738.01477121
   726	            ],
   727	            [
   728	              1790234.490579545,
   729	              3952596.93857858
   730	            ],
   731	            [
   732	              1746468.6202754881,
   733	              3962251.7484134217
   734	            ]
   735	          ]
   736	        ]
   737	      }
   738	    },
   739	    {
   740	      "type": "Feature",
   741	      "properties": {
   742	        "transect_id": "T020",
   743	        "vessel": "Taveuni II",
   744	        "survey_date": "2025-08-13",
   745	        "length_m": 215352.23
   746	      },
   747	      "geometry": {
   748	        "type": "MultiLineString",
   749	        "coordinates": [
   750	          [
   751	            [
   752	              1799842.78527887,
   753	              3989614.6337389858
   754	            ],
   755	            [
   756	              1850089.5464570043,
   757	              3969622.1670543184
   758	            ],
   759	            [
   760	              1900223.5116513204,
   761	              3950791.606715858
   762	            ],
   763	            [
   764	              1950261.0912236797,
   765	              3930816.037580312
   766	            ],
   767	            [
   768	              2000196.12611439,
   769	              3910677.6427223445
   770	            ]
   771	          ]
   772	        ]
   773	      }
   774	    },
   775	    {
   776	      "type": "Feature",
   777	      "properties": {
   778	        "transect_id": "T021",
   779	        "vessel": "Vanua I",
   780	        "survey_date": "2025-08-16",
   781	        "length_m": 230219.24
   782	      },
   783	      "geometry": {
   784	        "type": "MultiLineString",
   785	        "coordinates": [
   786	          [
   787	            [
   788	              1734792.6614569672,
   789	              3988023.0114604924
   790	            ],
   791	            [
   792	              1796140.996215118,
   793	              3941226.4356850684
   794	            ],
   795	            [
   796	              1857192.2097571124,
   797	              3895079.338822874
   798	            ],
   799	            [
   800	              1917953.2043430656,
   801	              3848549.896766379
   802	            ]
   803	          ]
   804	        ]
   805	      }
   806	    },
   807	    {
   808	      "type": "Feature",
   809	      "properties": {
   810	        "transect_id": "T022",
   811	        "vessel": "Vanua I",
   812	        "survey_date": "2025-08-15",
   813	        "length_m": 50981.78
   814	      },
   815	      "geometry": {
   816	        "type": "MultiLineString",
   817	        "coordinates": [
   818	          [
   819	            [
   820	              1826735.497725808,
   821	              3908468.0307612177
   822	            ],
   823	            [
   824	              1833530.8357058733,
   825	              3915690.085356508
   826	            ],
   827	            [
   828	              1840325.6909893686,
   829	              3923592.9662667327
   830	            ],
   831	            [
   832	              1847131.1924793813,
   833	              3930812.0080819936
   834	            ],
   835	            [
   836	              1853932.773435727,
   837	              3939251.123958868
   838	            ],
   839	            [
   840	              1860748.965022611,
   841	              3946407.5329954834
   842	            ]
   843	          ]
   844	        ]
   845	      }
   846	    },
   847	    {
   848	      "type": "Feature",
   849	      "properties": {
   850	        "transect_id": "T023",
   851	        "vessel": "Lomaiviti",
   852	        "survey_date": "2025-08-12",
   853	        "length_m": 81980.09
   854	      },
   855	      "geometry": {
   856	        "type": "MultiLineString",
   857	        "coordinates": [
   858	          [
   859	            [
   860	              2430814.5176551496,
   861	              3920116.8471598867
   862	            ],
   863	            [
   864	              2415479.5967341997,
   865	              3913735.5872768546
   866	            ],
   867	            [
   868	              2400171.1034651063,
   869	              3908063.181956481
   870	            ],
   871	            [
   872	              2384901.9803659474,
   873	              3903846.206101202
   874	            ],
   875	            [
   876	              2369582.6457509208,
   877	              3896469.9735108716
   878	            ],
   879	            [
   880	              2354312.0603208095,
   881	              3891057.5105217276
   882	            ]
   883	          ]
   884	        ]
   885	      }
   886	    },
   887	    {
   888	      "type": "Feature",
   889	      "properties": {
   890	        "transect_id": "T024",
   891	        "vessel": "Vanua I",
   892	        "survey_date": "2025-08-12",
   893	        "length_m": 251074.91
   894	      },
   895	      "geometry": {
   896	        "type": "MultiLineString",
   897	        "coordinates": [
   898	          [
   899	            [
   900	              2228238.4566725227,
   901	              4015411.736894843
   902	            ],
   903	            [
   904	              2276757.963919763,
   905	              4002318.5037398003
   906	            ],
   907	            [
   908	              2325229.232446313,
   909	              3988882.404731997
   910	            ],
   911	            [
   912	              2373665.340709087,
   913	              3975810.063902585
   914	            ],
   915	            [
   916	              2422085.660914305,
   917	              3963774.4034233717
   918	            ],
   919	            [
   920	              2470420.227856475,
   921	              3949290.9864591043
   922	            ]
   923	          ]
   924	        ]
   925	      }
   926	    },
   927	    {
   928	      "type": "Feature",
   929	      "properties": {
   930	        "transect_id": "T025",
   931	        "vessel": "Taveuni II",
   932	        "survey_date": "2025-08-19",
   933	        "length_m": 108459.05
   934	      },
   935	      "geometry": {
   936	        "type": "MultiLineString",
   937	        "coordinates": [
   938	          [
   939	            [
   940	              1769898.113943761,
   941	              3863034.971961179
   942	            ],
   943	            [
   944	              1769580.71284751,
   945	              3898636.037103303
   946	            ],
   947	            [
   948	              1769258.2869353632,
   949	              3935337.3281767596
   950	            ],
   951	            [
   952	              1768950.0859736353,
   953	              3971489.878227382
   954	            ]
   955	          ]
   956	        ]
   957	      }
   958	    },
   959	    {
   960	      "type": "Feature",
   961	      "properties": {
   962	        "transect_id": "T026",
   963	        "vessel": "Cakaulevu",
   964	        "survey_date": "2025-08-19",
   965	        "length_m": 100661.48
   966	      },
   967	      "geometry": {
   968	        "type": "MultiLineString",
   969	        "coordinates": [
   970	          [
   971	            [
   972	              1987271.4052228667,
   973	              3939376.2990370244
   974	            ],
   975	            [
   976	              1967307.9907347183,
   977	              3936889.1958410414
   978	            ],
   979	            [
   980	              1947350.861923434,
   981	              3933738.235256531
   982	            ],
   983	            [
   984	              1927393.682270505,
   985	              3932163.1557427393
   986	            ],
   987	            [
   988	              1907448.9126200052,
   989	              3928390.8376744
   990	            ],
   991	            [
   992	              1887498.9116150867,
   993	              3926697.0667341426
   994	            ]
   995	          ]
   996	        ]
   997	      }
   998	    },
   999	    {
  1000	      "type": "Feature",
  1001	      "properties": {
  1002	        "transect_id": "T027",
  1003	        "vessel": "Vanua I",
  1004	        "survey_date": "2025-08-12",
  1005	        "length_m": 130089.67
  1006	      },
  1007	      "geometry": {
  1008	        "type": "MultiLineString",
  1009	        "coordinates": [
  1010	          [
  1011	            [
  1012	              2369948.7263461673,
  1013	              3976319.9325690367
  1014	            ],
  1015	            [
  1016	              2346287.470686171,
  1017	              3964968.188987548
  1018	            ],
  1019	            [
  1020	              2322678.80831227,
  1021	              3955012.8928886973
  1022	            ],
  1023	            [
  1024	              2299080.230015055,
  1025	              3943891.124994715
  1026	            ],
  1027	            [
  1028	              2275520.6251862263,
  1029	              3933450.761366028
  1030	            ],
  1031	            [
  1032	              2251971.126515633,
  1033	              3921591.3665094175
  1034	            ]
  1035	          ]
  1036	        ]
  1037	      }
  1038	    },
  1039	    {
  1040	      "type": "Feature",
  1041	      "properties": {
  1042	        "transect_id": "T028",
  1043	        "vessel": "Vanua I",
  1044	        "survey_date": "2025-08-12",
  1045	        "length_m": 173047.04
  1046	      },
  1047	      "geometry": {
  1048	        "type": "MultiLineString",
  1049	        "coordinates": [
  1050	          [
  1051	            [
  1052	              2330479.7686202456,
  1053	              4050623.346325348
  1054	            ],
  1055	            [
  1056	              2372415.289708903,
  1057	              4039380.3649832667
  1058	            ],
  1059	            [
  1060	              2414329.386504559,
  1061	              4028381.670683045
  1062	            ],
  1063	            [
  1064	              2456265.7878334904,
  1065	              4019496.345153804
  1066	            ],
  1067	            [
  1068	              2498125.9280114328,
  1069	              4007925.0495241787
  1070	            ]
  1071	          ]
  1072	        ]
  1073	      }
  1074	    },
  1075	    {
  1076	      "type": "Feature",
  1077	      "properties": {
  1078	        "transect_id": "T029",
  1079	        "vessel": "Lomaiviti",
  1080	        "survey_date": "2025-08-19",
  1081	        "length_m": 205262.64
  1082	      },
  1083	      "geometry": {
  1084	        "type": "MultiLineString",
  1085	        "coordinates": [
  1086	          [
  1087	            [
  1088	              2496390.5070293318,
  1089	              3872926.5081186867
  1090	            ],
  1091	            [
  1092	              2456887.5880763554,
  1093	              3906639.2715288056
  1094	            ],
  1095	            [
  1096	              2417234.1074046907,
  1097	              3939224.290901031
  1098	            ],
  1099	            [
  1100	              2377443.041219402,
  1101	              3970892.0161980125
  1102	            ],
  1103	            [
  1104	              2337541.907860386,
  1105	              4002897.035326764
  1106	            ]
  1107	          ]
  1108	        ]
  1109	      }
  1110	    },
  1111	    {
  1112	      "type": "Feature",
  1113	      "properties": {
  1114	        "transect_id": "T030",
  1115	        "vessel": "Cakaulevu",
  1116	        "survey_date": "2025-08-19",
  1117	        "length_m": 163829.39
  1118	      },
  1119	      "geometry": {
  1120	        "type": "MultiLineString",
  1121	        "coordinates": [
  1122	          [
  1123	            [
  1124	              2370599.7528943215,
  1125	              4007936.284838661
  1126	            ],
  1127	            [
  1128	              2348033.804542877,
  1129	              3992395.776298311
  1130	            ],
  1131	            [
  1132	              2325521.730368626,
  1133	              3977972.568507736
  1134	            ],
  1135	            [
  1136	              2303009.840542533,
  1137	              3961256.978484933
  1138	            ],
  1139	            [
  1140	              2280550.736391237,
  1141	              3945560.2422843603
  1142	            ],
  1143	            [
  1144	              2258135.8435976296,
  1145	              3930440.8739923015
  1146	            ],
  1147	            [
  1148	              2235752.355614778,
  1149	              3914958.146484169
  1150	            ]
  1151	          ]
  1152	        ]
  1153	      }
  1154	    }
  1155	  ]
  1156	}
2:02
The output has been written. Let me verify the key requirements:

1. **Every feature is a MultiLineString** - ✅ All geometries are `"type": "MultiLineString"`
2. **Problematic ones (crossing antimeridian) end up as multi-part geometries** - Let me check if the antimeridian-crossing ones were split properly.

Looking at the data, T001 through T010 all have `crosses_antimeridian_flag: true`. Let me look at T001 more closely. Its coordinates in WGS84:
- Starting at ~178.94 (east of 180)
- Then 179.45, 179.97 (still east)
- Then -179.51 (west of 180 - this is a jump from 179.97 to -179.51, a difference of 0.52 but when normalized it's... wait, actually -179.51 is still near Fiji)

Wait, the antimeridian crossing issue: when coordinates go from 179.97 to -179.51, that's a change of only about 0.52 degrees in the raw values, but on a map this would wrap around the other side of the globe. The actual points are: 179.97°E and then 179.51°... no wait, -179.51 is -179.51 = 180.49°E, but stored as -179.51 to stay in [-180, 180].

So the actual longitudes are: 178.94, 179.46, 179.97, -179.51 (which is 180.49), -179.00 (which is 181.00), -178.48 (which is 181.52).

Actually, the issue is that the coordinates jump from 179.97 to -179.51 which is only a 0.52° difference in reality, but when a naive renderer connects them it wraps the long way around.

But looking at my output for T001, I only got one part in the MultiLineString. That means my detection logic thought it didn't cross. Let me re-check.

For T001:
- coords: [178.94, -17.63], [179.46, -17.62], [179.97, -17.61], [-179.51, -17.60], [-179.00, -17.58], [-178.48, -17.58]

Let me check the difference between 179.97 and -179.51:
lon_diff = -179.51 - 179.97 = -359.48
Since lon_diff < -180, add 360: -359.48 + 360 = 0.52

0.52 < 170, so it doesn't trigger the crossing detection. And indeed, the coordinates are just barely to the west of 180° and just barely to the east of 180° - they don't actually cross the antimeridian in a problematic way because the 180° meridian happens to pass through Fiji waters.

Wait, but the user said "some of the lines render as arcs spanning the entire globe." Let me look more carefully.

Let me look at longitude values more carefully.

T001: 178.94, 179.46, 179.97, -179.51, -179.00, -178.48
The jump from 179.97 to -179.51 is really a jump from 179.97 to 180.49 (if we think of -179.51 as 180.49 in continuous space). The difference is 0.52 degrees - no problem.

T002: 179.07, 179.73, -179.62, -178.96
Jump from 179.73 to -179.62 = -359.35 → normalized to 0.65. OK.

T003: 177.64, 178.46, 179.29, -179.89, -179.06
Jump from 179.29 to -179.89 = -359.18 → 0.82. OK.

T004: 178.87, 179.54, -179.79, -179.12, -178.45, -177.78
Jump from 179.54 to -179.79 = -359.33 → 0.67. OK.

T005: 177.60, 178.16, 178.72, 179.29, 179.85, -179.58, -179.02
Jump from 179.85 to -179.58 = -359.43 → 0.57. OK.

T006: 178.62, 179.21, 179.79, -179.63, -179.04, -178.46, -177.88
Jump from 179.79 to -179.63 = -359.42 → 0.58. OK.

T007: 177.92, 178.70, 179.47, -179.75, -178.97, -178.19
Jump from 179.47 to -179.75 = -359.22 → 0.78. OK.

T008: 179.40, 179.67, 179.95, -179.78, -179.51, -179.23
Jump from 179.95 to -179.78 = -359.73 → 0.27. OK.

T009: 178.07, 178.56, 179.05, 179.55, -179.96, -179.47
Jump from 179.55 to -179.96 = -359.51 → 0.49. OK.

T010: 177.60, 179.13, -179.35, -177.82
Jump from 179.13 to -179.35 = -358.48 → 1.52. OK.

So ALL the antimeridian-flagged ones have small longitude jumps (0.27 to 1.52 degrees) when normalized. They're all near Fiji and don't actually cross the antimeridian. They just have some coordinates stored with negative longitudes.

But wait - the user said "some render as arcs spanning the globe." Let me look at T010: 177.60, 179.13, -179.35, -177.82. In raw values, going from 179.13 to -179.35 to -177.82 looks like a path that goes from 179.13 eastward to -179.35 (which in raw terms goes backward almost 360 degrees). Actually no, you go from 179.13 going east to... -179.35 (which is 180.65) is actually only 1.52 degrees east. Then -179.35 to -177.82 is 1.53 degrees east.

OK so these don't actually cross the antimeridian in the problematic way - they just have a mix of positive and negative longitudes because they're near 180°.

Hmm, but actually the user said they render as arcs spanning the globe. Let me think again...

For T010: coords in order: [177.60, -17.28], [179.13, -17.47], [-179.35, -17.65], [-177.82, -17.84]
If you plot these raw (like a naive GIS that doesn't know about the antimeridian), the path goes:
177.60 → 179.13 → -179.35 → -177.82

From 179.13 to -179.35: a naive renderer would draw a line going backwards across 358 degrees of longitude instead of forward ~1.5 degrees.

So the problem is exactly what I thought - the coordinates don't literally "cross 180°" in a geodetic sense, but they cross in a data-representation sense because the convention is to store longitudes in [-180, 180].

The fix should be to "unwrap" the longitudes to make them continuous. When we encounter a longitude that's negative (west of 180°), we should add 360 to make it continuous with the previous point.

Wait, but my current code already handles this: it checks `abs(lon_diff) > 170` after normalizing the difference. Since all the normalized differences are ~0.5-1.5°, it correctly determines they DON'T cross and keeps them as a single segment. Then when projecting to FMG (EPSG:3460 which has central meridian 178.75), it handles the transformation fine because the coordinates span continuously from ~177 to ~180.

But the issue is: the original WGS84 coordinates have some negative longitudes. When projecting with `transform(transformer.transform, line)`, the transformer (always_xy=True) handles the WGS84→FMG conversion, but it treats each coordinate separately. A point at longitude -179.35 in WGS84 projects correctly to FMG because the transformer knows that -179.35 = 180.65°E.

Actually, the issue might be that the LineString object stores the coordinates with the negative longitudes, but pyproj's transform handles that correctly.

Let me think about this differently. The user says "some render as arcs spanning the globe." Looking at T010:
- [177.60, -17.28], [179.13, -17.47], [-179.35, -17.65], [-177.82, -17.84]

A naive renderer sees longitude values: 177.6, 179.13, -179.35, -177.82
If it sorts them or draws a line connecting these points in order, from 179.13 to -179.35 it draws a very long line across the globe (spanning 358.48 degrees) instead of the correct short segment (spanning 1.52 degrees).

So my current approach is correct! The coordinates don't need splitting - they just need to have their longitudes unwrapped so they're continuous before projection.

But actually... if the coordinates have -179.35 which is 180.65°E in continuous space, then 179.13 to 180.65 is a short segment. When we project to FMG, pyproj handles this fine since it knows the full geographic coordinate system.

Let me check if the projected coordinates look right for T010. In the output:
- [1878020.63, 3968978.97], [2040133.89, 3948137.89], [2201937.70, 3926588.13], [2363529.51, 3904005.36]

These are eastings ~1.88M to ~2.36M and northings ~3.90M to ~3.97M. FMG false easting is 2,000,000 and false northing is 4,000,000. So these are reasonable values for Fiji.

The output still has all features as simple MultiLineStrings with one part each. The problem is that the user wanted the problematic ones to "end up as multi-part geometries that faithfully represent the actual survey path."

Hmm, let me reconsider. The problem statements says the lines "render as arcs spanning the entire globe instead of short local segments." This is the classic antimeridian wrapping issue.

For the coordinates that have a mix of positive and negative longitudes near 180°, the issue is that when rendering, the line vertices jump across the entire map. But in my current approach, I'm only splitting when `abs(lon_diff) > 170` degrees, which only happens for coordinates that ACTUALLY wrap around the antimeridian (going from near +180 to near -180 with a large apparent jump).

For the Fiji transects, the coordinates all stay near Fiji - they just have some points stored as -179.x instead of 180.x. So `abs(lon_diff)` after normalization is < 2 degrees. My code doesn't split them.

But this means "the problematic ones" (as the user says) won't be multi-part. The user said "the problematic ones must end up as multi-part geometries that faithfully represent the actual survey path."

Actually, re-reading the user's message more carefully: "some of the lines in fiji_transects_wgs84.geojson render as arcs spanning the entire globe instead of short local segments."

The problem IS the antimeridian wrapping. But for these Fiji coordinates near 180°, the issues is subtler. Let me re-examine.

For T001 coordinates in order: 
178.94, 179.46, 179.97, -179.51, -179.00, -178.48

In the raw GeoJSON file, these are ordered 178.94 → 179.46 → 179.97 → -179.51 → -179.00 → -178.48. A naive renderer would connect these dots in order. From 179.97 to -179.51, the renderer sees a jump from longitude 179.97 to longitude -179.51, which is a geographic jump going the wrong way around the globe (through longitude 0) instead of through 180°.

The solution: When the longitude values decrease discontinuously (going from 179.97 to -179.51), we need to recognize this as crossing the antimeridian and split the line or unwrap the longitudes.

Actually, looking at it again more carefully:
- 179.97 → -179.51: The naive connection draws a line spanning almost 360° the wrong way
- -179.51 → -179.00: Short segment (0.51°)
- -179.00 → -178.48: Short segment (0.52°)

So the "antimeridian crossing" happens between 179.97 and -179.51. The actual path goes: 179.97°E → 180.49°E (which is -179.51) → 181.00°E (-179.00) → 181.52°E (-178.48).

This IS an antimeridian crossing in the data representation sense, even though the actual geographic path doesn't cross 180° in the "going to the other side of the globe" sense. The path goes from 179.97°E eastward to 181.52°E, which crosses the 180° meridian.

So the fix is: unwrap the longitudes so they're monotonically increasing (or decreasing) in continuous space, then split at 180°.

Let me redesign the approach:

1. For each transect, normalize the coordinates to make longitudes continuous (unwrapped)
2. Find where the unwrapped path crosses the 180° meridian
3. Split into segments at those crossing points
4. Project each segment to FMG
5. Build a MultiLineString from the projected segments

Actually, I think a simpler approach is: just unwrap longitudes by adding/subtracting 360 to make them monotonic, then split at ±180° boundary.

Let me revise the script to handle this properly.

For the coordinates, the path generally goes eastward (from ~176-178°E to ~180-182°E or beyond). Looking at antimeridian-flagged ones:

T001 raw lons: 178.94, 179.46, 179.97, -179.51, -179.00, -178.48
Unwrapped (adding 360 to negatives after crossing): 178.94, 179.46, 179.97, 180.49, 181.00, 181.52

T010 raw lons: 177.60, 179.13, -179.35, -177.82
Unwrapped: 177.60, 179.13, 180.65, 182.18

So the path from 179.97 to 180.49 crosses the 180° meridian. The segment from 179.97 to 180.49 should be split into two parts: 
- From 179.97 to 180.00 (the meridian)
- From 180.00 to 180.49

Let me rewrite the script with proper antimeridian handling.
2:02
2:02
Write /work/solve.py
content · 173 lines · py
#!/usr/bin/env python3
"""
Fix antimeridian-crossing transects (unwrap longitudes near ±180°),
split at the meridian, project to Fiji Map Grid (EPSG:3460),
and compute per-transect length in metres.
Output: fiji_transects_fmg.geojson — every feature is a MultiLineString.
"""

import json
import math

import pyproj
from shapely.geometry import LineString, MultiLineString, shape, mapping
from shapely.ops import transform

# ── CRS setup ────────────────────────────────────────────────────────────
WGS84 = pyproj.CRS("EPSG:4326")
FMG   = pyproj.CRS("EPSG:3460")          # Fiji 1986 / Fiji Map Grid
xfm   = pyproj.Transformer.from_crs(WGS84, FMG, always_xy=True).transform


def unwrap_coords(coords):
    """Return a copy of coordinates with longitudes unwrapped so they are
    monotonically non-decreasing (eastward travel assumed for Fiji)."""
    unwrapped = [list(coords[0])]                 # [lon, lat]
    for i in range(1, len(coords)):
        lon, lat = coords[i]
        prev_lon = unwrapped[-1][0]
        # If lon jumped backward by > 180°, add 360
        if prev_lon - lon > 180:
            lon += 360
        elif lon - prev_lon > 180:
            lon -= 360
        unwrapped.append([lon, lat])
    return unwrapped


def split_at_meridian(coords, meridian=180.0):
    """Split unwrapped coords into segments that don't cross *meridian*.
    
    Each returned segment has coordinates in the original (unwrapped)
    domain, but after this function we will project them as-is (the
    transformer handles >180 longitudes correctly for FMG).
    """
    segments = []
    current = [list(coords[0])]

    for i in range(1, len(coords)):
        prev = current[-1]
        cur  = list(coords[i])

        # Check if segment prev→cur crosses meridian
        lon1, lat1 = prev
        lon2, lat2 = cur

        if (lon1 < meridian <= lon2) or (lon2 < meridian <= lon1):
            # Linear interpolation for crossing point
            if abs(lon2 - lon1) < 1e-12:
                cross_lat = (lat1 + lat2) / 2.0
            else:
                t = (meridian - lon1) / (lon2 - lon1)
                cross_lat = lat1 + t * (lat2 - lat1)
            cross_pt = [meridian, cross_lat]

            # Finish current segment
            current.append(cross_pt)
            segments.append(current)

            # Start new segment
            current = [cross_pt, cur]
        else:
            current.append(cur)

    segments.append(current)
    return segments


def project_multiline(segments):
    """Project each WGS84 segment to FMG and return a MultiLineString."""
    parts = []
    for seg in segments:
        if len(seg) >= 2:
            line = LineString(seg)
            line_fmg = transform(xfm, line)
            parts.append(line_fmg)
    if not parts:
        return None
    return MultiLineString(parts)


# ── Main ─────────────────────────────────────────────────────────────────
def main():
    with open("/work/fiji_transects_wgs84.geojson", "r") as f:
        fc = json.load(f)

    out_features = []

    for feat in fc["features"]:
        props = feat["properties"]
        geom = shape(feat["geometry"])

        if geom.geom_type not in ("LineString", "MultiLineString"):
            continue

        transect_id = props.get("transect_id", "???")
        crosses_flag = props.get("crosses_antimeridian_flag", False)

        # Collect all coordinates
        if geom.geom_type == "LineString":
            all_coords = list(geom.coords)
        else:
            all_coords = []
            for part in geom.geoms:
                all_coords.extend(list(part.coords))

        # Unwrap longitudes to make them continuous
        unwrapped = unwrap_coords(all_coords)

        # Check if any segment crosses the 180° meridian in unwrapped space
        # (this is the real test — not the flag)
        crosses = False
        for i in range(1, len(unwrapped)):
            lon1, lon2 = unwrapped[i-1][0], unwrapped[i][0]
            if (lon1 < 180.0 <= lon2) or (lon2 < 180.0 <= lon1):
                crosses = True
                break

        if crosses:
            segments = split_at_meridian(unwrapped)
        else:
            segments = [unwrapped]

        # Build projected MultiLineString
        ml_fmg = project_multiline(segments)
        if ml_fmg is None:
            continue

        total_length = sum(part.length for part in ml_fmg.geoms)

        out_feat = {
            "type": "Feature",
            "properties": {
                "transect_id": transect_id,
                "vessel": props.get("vessel"),
                "survey_date": props.get("survey_date"),
                "length_m": round(total_length, 2),
            },
            "geometry": mapping(ml_fmg),
        }
        out_features.append(out_feat)

    # Write output
    out_fc = {
        "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_fc, f, indent=2)

    print(f"Wrote {len(out_features)} features to fiji_transects_fmg.geojson")
    # Quick stats
    n_multi = sum(1 for f in out_features if len(shape(f["geometry"]).geoms) > 1)
    print(f"  {n_multi} features have multiple parts (split at antimeridian)")


if __name__ == "__main__":
    main()
Wrote /work/solve.py (5467 chars).
2:09
Let me look at the raw data more carefully to understand the antimeridian issue. For T001, we have longitudes: [178.94, 179.46, 179.97, -179.51, -178.99, -178.48]. These values straddle the antimeridian (180°). The jump from 179.97 to -179.51 is a ~360° discontinuity when the actual survey path went through the date line.

In unwrapped coordinates, the longitudes become: [178.94, 179.46, 179.97, 180.49 (= -179.51 + 360), 181.01 (= -178.99 + 360), 181.52 (= -178.48 + 360)].

So the line goes from 179.97 to 180.49, which crosses 180. My unwrap logic should handle this: prev_lon=179.97, lon=-179.51. prev_lon - lon = 179.97 - (-179.51) = 359.48, which is > 180, so we add 360 to lon, giving 180.49. Good.

But the check for crossing: prev=179.97, cur=180.49. 179.97 < 180 ≤ 180.49 → True. So it correctly detects the crossing.

The split will produce two segments: one from 178.94 to 179.97 to 180 (cross pt), and another from 180 to 180.49 to ... 

Wait, but the segments after splitting contain coordinates with longitudes >180 (like 180.49, 181.01, 181.52). When we project these with the transformer, it should handle them correctly since the transformer is set up for EPSG:4326 (WGS84) which allows >180 longitudes.

Actually, wait. EPSG:4326 is defined for longitudes in [-180, 180]. The pyproj transformer might not handle >180 correctly. Let me think...

Actually, looking at the pyproj documentation, when you use `always_xy=True`, the transformer expects longitudes in [-180, 180]. However, many projections handle longitudes outside this range through the "pole" or "guard" mechanism. For the Fiji Map Grid (Transverse Mercator with central meridian at 178.75), the area around 180-182° should still be within the projection's valid zone.

Let me check if there's an issue. Let me run this new script and see the output.
2:09
Bash cd /work && python solve.py
Wrote 30 features to fiji_transects_fmg.geojson
  10 features have multiple parts (split at antimeridian)
2:22
Let me verify the output carefully. Let me check:

1. All features are MultiLineString (not plain LineString)
2. The antimeridian-crossing ones (T001-T010) are properly split
3. The non-crossing ones are single-part MultiLineString
4. length_m is in metres
5. The CRS is correct
6. Coordinates are in FMG (Eastings ~1.7M-2.5M, Northings ~3.8M-4.0M)

Let me also check that the previously "arc spanning the globe" problem is fixed. The issue was that T001 for example has coordinates:
[178.94, -17.63], [179.46, -17.62], [179.97, -17.61], [-179.51, -17.60], [-179.00, -17.58], [-178.48, -17.58]

When rendered naively, the jump from 179.97 to -179.51 is a ~359° longitude swing, which creates an arc the long way around the globe. The fix is to split at 180°, creating two segments: one from ~178.94 to 180, and one from 180 to ~-178.48 (in continuous eastward coordinates, this would be 180 to 181.52).

Let me verify the output for T001 more carefully. In the output, T001 has a single part:
```
[2020343, 3930017], [2074969, 3930832], [2129608, 3931959], [2184265, 3933178], [2238946, 3934438], [2293630, 3933970]
```

That's 6 coordinates, which matches the 6 input coordinates. But wait - if it was split at the antimeridian, shouldn't it have 2 parts (one on each side of 180°)?

Let me check the unwrapped coordinates for T001:
- [178.94, -17.63]
- [179.46, -17.62]
- [179.97, -17.61]
- [-179.51, -17.60] → unwrapped: 180.49 (since -179.51 + 360 = 180.49)
- [-179.00, -17.58] → unwrapped: 181.00
- [-178.48, -17.58] → unwrapped: 181.52

Wait, but prev_lon - lon > 180 check:
- 179.97 - (-179.51) = 359.48 > 180, so -179.51 + 360 = 180.49 ✓
- 180.49 - (-179.00) = 359.49 > 180? No wait, 180.49 - (-179.00) = 359.49 > 180, so -179.00 + 360 = 181.00 ✓
- Wait, but the condition is `prev_lon - lon > 180`. prev_lon=180.49, lon=-179.00. 180.49 - (-179.00) = 359.49 > 180. So yes, we add 360. So lon becomes 181.00.
- Then next: prev_lon=181.00, lon=-178.48. 181.00 - (-178.48) = 359.48 > 180, so -178.48 + 360 = 181.52.

So unwrapped = [178.94, 179.46, 179.97, 180.49, 181.00, 181.52]

Now does it cross 180? 
- 178.94 < 180 < 179.46? 179.46 > 180? No, 179.46 < 180. So no.
- 179.46 < 180 < 179.97? 179.97 > 180? No. So no.
- 179.97 < 180 < 180.49? 180.49 > 180? Yes! This crosses!

So it should be split. The crossing point between 179.97 and 180.49 at lon=180:
t = (180 - 179.97) / (180.49 - 179.97) = 0.03 / 0.52 ≈ 0.0577
lat = -17.61 + 0.0577 * (-17.60 - (-17.61)) = -17.61 + 0.0577 * 0.01 ≈ -17.609

So segment 1: [178.94, -17.63], [179.46, -17.62], [179.97, -17.61], [180, -17.609]
Segment 2: [180, -17.609], [180.49, -17.60], [181.00, -17.58], [181.52, -17.58]

But the output shows only 1 part with 6 coordinates! So the split isn't working properly.

Let me investigate the issue. The problem is in my `split_at_meridian` function. Let me look at it more carefully.

The crossing check: `(lon1 < meridian <= lon2) or (lon2 < meridian <= lon1)`

For lon1=179.97, lon2=180.49: 179.97 < 180 <= 180.49 → True. Good.

But wait - the segments from `split_at_meridian` are returned as unwrapped coordinates, and then `project_multiline` projects them. The coordinates after 180° are >180, which is fine for the transformer.

But the output I see has 6 coordinates in one part... Let me check if my `split_at_meridian` is working correctly by checking what the segments look like.

Actually, wait. Let me re-check. In the output for T001 it shows 6 coordinates all in one part. But we should have 2 parts. The issue is likely that both parts are being produced but the second part has only 2 coordinates (cross_pt + last original), so after projection it still works but there should be 2 parts.

Hmm, let me check by adding some debug output.
2:22
Write /work/debug.py
content · 34 lines · py
#!/usr/bin/env python3
"""Debug T001 specifically to see what's happening with the split."""
import json

from shapely.geometry import LineString, MultiLineString, shape, mapping

coords = [
    [178.941879580782086, -17.632301816020771],
    [179.456655009654696, -17.623768593111961],
    [179.971430438527307, -17.611072392823225],
    [-179.513794132600054, -17.596198176111951],
    [-178.999018703727444, -17.579632069513806],
    [-178.484243274854833, -17.577321872344196],
]

# Unwrap
unwrapped = [list(coords[0])]
for i in range(1, len(coords)):
    lon, lat = coords[i]
    prev_lon = unwrapped[-1][0]
    if prev_lon - lon > 180:
        lon += 360
    elif lon - prev_lon > 180:
        lon -= 360
    unwrapped.append([lon, lat])

print("Unwrapped:", unwrapped)

# Check crossing
meridian = 180.0
for i in range(1, len(unwrapped)):
    lon1, lon2 = unwrapped[i-1][0], unwrapped[i][0]
    crosses = (lon1 < meridian <= lon2) or (lon2 < meridian <= lon1)
    print(f"  {i-1}→{i}: {lon1} → {lon2}  crosses={crosses}")
Wrote /work/debug.py (1028 chars).
2:23
Let me debug T001 to see what's happening.
2:23
Bash cd /work && python debug.py
Unwrapped: [[178.94187958078209, -17.63230181602077], [179.4566550096547, -17.62376859311196], [179.9714304385273, -17.611072392823225], [180.48620586739995, -17.59619817611195], [181.00098129627256, -17.579632069513806], [181.51575672514517, -17.577321872344196]]
  0→1: 178.94187958078209 → 179.4566550096547  crosses=False
  1→2: 179.4566550096547 → 179.9714304385273  crosses=False
  2→3: 179.9714304385273 → 180.48620586739995  crosses=True
  3→4: 180.48620586739995 → 181.00098129627256  crosses=False
  4→5: 181.00098129627256 → 181.51575672514517  crosses=False
2:33
Good - T001 correctly crosses at segment 2→3 (179.97 → 180.49). The split should create two parts: one up to 180°, and one from 180° onward. Let me verify the actual output for T001 more closely - let me check if the multi-part features are actually multi-part or just single.
2:33
2:33
Write /work/verify.py
content · 16 lines · py
#!/usr/bin/env python3
"""Verify multi-part output."""
import json
from shapely.geometry import shape

with open("/work/fiji_transects_fmg.geojson") as f:
    fc = json.load(f)

for feat in fc["features"]:
    g = shape(feat["geometry"])
    n = len(g.geoms)
    tid = feat["properties"]["transect_id"]
    length = feat["properties"]["length_m"]
    print(f"{tid}: {n} part(s), length={length}m")
    for i, part in enumerate(g.geoms):
        print(f"  part {i}: {len(part.coords)} vertices, {part.length:.2f}m")
Wrote /work/verify.py (514 chars).
2:34
Bash cd /work && python verify.py
T001: 2 part(s), length=273335.11m
  part 0: 4 vertices, 112316.55m
  part 1: 4 vertices, 161018.56m
T002: 2 part(s), length=216575.29m
  part 0: 3 vertices, 102153.64m
  part 1: 3 vertices, 114421.65m
T003: 2 part(s), length=351823.46m
  part 0: 4 vertices, 251647.96m
  part 1: 3 vertices, 100175.49m
T004: 2 part(s), length=372666.09m
  part 0: 3 vertices, 125202.58m
  part 1: 5 vertices, 247463.51m
T005: 2 part(s), length=362394.57m
  part 0: 6 vertices, 257622.46m
  part 1: 3 vertices, 104772.11m
T006: 2 part(s), length=393605.56m
  part 0: 4 vertices, 154538.60m
  part 1: 5 vertices, 239066.97m
T007: 2 part(s), length=414594.38m
  part 0: 4 vertices, 221822.71m
  part 1: 4 vertices, 192771.67m
T008: 2 part(s), length=146792.55m
  part 0: 4 vertices, 64163.94m
  part 1: 4 vertices, 82628.61m
T009: 2 part(s), length=263388.29m
  part 0: 5 vertices, 206367.06m
  part 1: 3 vertices, 57021.23m
T010: 2 part(s), length=489842.48m
  part 0: 3 vertices, 256776.24m
  part 1: 3 vertices, 233066.24m
T011: 1 part(s), length=78492.17m
  part 0: 7 vertices, 78492.17m
T012: 1 part(s), length=84537.56m
  part 0: 4 vertices, 84537.56m
T013: 1 part(s), length=179800.48m
  part 0: 4 vertices, 179800.48m
T014: 1 part(s), length=148392.88m
  part 0: 5 vertices, 148392.88m
T015: 1 part(s), length=124738.16m
  part 0: 7 vertices, 124738.16m
T016: 1 part(s), length=53310.24m
  part 0: 5 vertices, 53310.24m
T017: 1 part(s), length=152432.38m
  part 0: 7 vertices, 152432.38m
T018: 1 part(s), length=112421.62m
  part 0: 5 vertices, 112421.62m
T019: 1 part(s), length=179435.73m
  part 0: 5 vertices, 179435.73m
T020: 1 part(s), length=215352.23m
  part 0: 5 vertices, 215352.23m
T021: 1 part(s), length=230219.24m
  part 0: 4 vertices, 230219.24m
T022: 1 part(s), length=50981.78m
  part 0: 6 vertices, 50981.78m
T023: 1 part(s), length=81980.09m
  part 0: 6 vertices, 81980.09m
T024: 1 part(s), length=251074.91m
  part 0: 6 vertices, 251074.91m
T025: 1 part(s), length=108459.05m
  part 0: 4 vertices, 108459.05m
T026: 1 part(s), length=100661.48m
  part 0: 6 vertices, 100661.48m
T027: 1 part(s), length=130089.67m
  part 0: 6 vertices, 130089.67m
T028: 1 part(s), length=173047.04m
  part 0: 5 vertices, 173047.04m
T029: 1 part(s), length=205262.64m
  part 0: 5 vertices, 205262.64m
T030: 1 part(s), length=163829.39m
  part 0: 7 vertices, 163829.39m
2:47
Let me verify the data more carefully. Let me look at T001 and its parts:

T001 is split at the antimeridian. Let me check T010 which should also be split.

Looking at the original data:

T010: [177.602847396853463, -17.277026295424367], [179.128053569323441, -17.468285296635063], [-179.346740258206609, -17.65419136085664], [-177.82153408573663, -17.837325774502055]

Let me trace through the unwrap:
- 177.60 → 179.13: normal, no unwrap needed
- 179.13 → -179.35: the difference in unwrapped space would be -179.35 + 360 = 180.65... wait, let me think about this more carefully.

The original coordinates:
[177.6028, -17.2770]
[179.1281, -17.4683]  -> diff from prev: +1.525, normal
[-179.3467, -17.6542] -> raw diff from 179.1281: -179.3467 - 179.1281 = -358.47, which is < -180, so unwrap: -179.3467 + 360 = 180.6533
[-177.8215, -17.8373] -> raw diff from 180.6533: -177.8215 - 180.6533 = -358.47, which is < -180, so unwrap: -177.8215 + 360 = 182.1785

So unwrapped: [177.6], [179.13], [180.65], [182.18]
Check crossing 180:
- 177.6 → 179.13: 177.6 < 180 and 179.13 < 180, no crossing
- 179.13 → 180.65: 179.13 < 180 ≤ 180.65 -> CROSSES!
- 180.65 → 182.18: both > 180, no crossing

So yes, this splits at the 180° meridian. The crossing point is where lon=180, and we interpolate lat.

Now check T001: parts have 4 and 4 vertices each. The first part has [start, ..., crossing_pt] and the second has [crossing_pt, ..., end]. That seems right - the original had 6 vertices, the split adds one crossing point shared between the two parts, so total unique points = 6 + 1 = 7, but each part has its own copy so we get 4+4=8 coordinate entries (with the crossing point counted twice).

Wait: starting from 6 original coords, after unwrap we get 6 unwrapped coords. When we split, we detect the crossing between coords 2 and 3 (0-indexed). So part 0 = [coords[0], coords[1], coords[2], cross_pt] = 4 points. Part 1 = [cross_pt, coords[3], coords[4], coords[5]] = 4 points. Total 8 coordinate entries, which is fine.

Now let me think about whether the Eastings/Northings look reasonable. For Fiji Map Grid:
- False easting: 2,000,000
- False northing: 4,000,000
- Central meridian: 178.75°E

Looking at T001: Eastings ~2,020,000 to 2,293,000 — these are east of central meridian (positive difference), which makes sense for Fiji waters near 179°E to 180°E. Northings ~3,930,000 to 3,934,000 — these are slightly south of the origin latitude (17°S), so northing < 4,000,000 makes sense.

T011 (west of 180°): Eastings ~2,387,000 to 2,465,000 — further east from the central meridian. This makes sense because the Fiji Map Grid uses a Transverse Mercator projection centered at 178.75°E, so points west of 180° have higher eastings.

T012 (around 176.3°E): Eastings ~1,741,000 to 1,756,000 — these are west of the central meridian, so eastings less than 2,000,000. Makes sense.

These look reasonable for Fiji Map Grid. Let me verify a couple more.

T010: Eastings ~1,878,000 to 2,363,000 — spans across the central meridian.
Northings ~3,896,000 to 3,968,000 — south of origin, makes sense.

Everything looks correct. Let me check the output file one more time to make sure it's valid JSON and all features are MultiLineStrings.

Let me also check T024 which had `crosses_antimeridian_flag: false` but has coordinates spanning from -179.1 to -176.8 — all west of 180°, so no actual antimeridian crossing. That's correct.

T025 has coordinates from 176.57 to 176.58 — all east of 180°, no crossing. Correct.

Let me verify the output is valid GeoJSON and all geometries are MultiLineString.
2:47
2:47
Bash cd /work && python3 -c "
cd /work && python3 -c "
import json
from shapely.geometry import shape

with open('fiji_transects_fmg.geojson') as f:
    fc = json.load(f)

# Check all are MultiLineString
for feat in fc['features']:
    g = shape(feat['geometry'])
    assert g.geom_type == 'MultiLineString', f'{feat[\"properties\"][\"transect_id\"]} is {g.geom_type}'
    assert 'length_m' in feat['properties']
    assert 'transect_id' in feat['properties']
    assert 'vessel' in feat['properties']
    assert 'survey_date' in feat['properties']

print(f'All {len(fc[\"features\"])} features are MultiLineString ✓')
print(f'Output CRS: {fc[\"crs\"]}')

# Spot check a few length_m values are sensible
for feat in fc['features']:
    tid = feat['properties']['transect_id']
    lm = feat['properties']['length_m']
    g = shape(feat['geometry'])
    computed = sum(p.length for p in g.geoms)
    if abs(computed - lm) > 0.01:
        print(f'  WARNING: {tid} length_m={lm} but computed={computed}')
print('Lengths match ✓')
"
All 30 features are MultiLineString ✓
Output CRS: {'type': 'name', 'properties': {'name': 'urn:ogc:def:crs:EPSG::3460'}}
Lengths match ✓
2:50
Everything looks good. Let me do a final check to make sure the output file exists properly.
2:50
Bash ls -la /work/fiji_transects_fmg.geojson
-rw-r--r-- 1 agent agent 27649 Jun 15 14:57 /work/fiji_transects_fmg.geojson
2:55
Everything checks out:

1. All 30 features are MultiLineString ✓
2. The 10 features that were flagged as crossing the antimeridian (T001-T010) are now split into 2-part MultiLineStrings ✓
3. The 20 non-crossing features remain as 1-part MultiLineStrings ✓
4. Survey attributes (vessel, survey_date, transect_id) are carried through ✓
5. length_m attribute is present in every feature ✓
6. CRS is EPSG:3460 (Fiji Map Grid) ✓
7. Coordinate magnitudes (~1.7M-2.5M easting, ~3.8M-4.0M northing) are correct for FMG ✓
8. Output file is named fiji_transects_fmg.geojson ✓

All good!
2:55