fio-l2-cairo-mixedgeom-split
Map (reference ← swipe → agent)
0:00
Heritage analyst here. I've put together `heritage_sites.geojson` from my own field work, and every site in it is several features sharing a `site_id`. I need to load the data into our desktop tool, which only ingests typed GPKG layers, no mixed-geometry files, and every feature has to be single-part. Can you give me back `heritage.gpkg` with three layers named `points`, `lines`, and `polygons`, keeping the `site_id` on every feature so I don't lose the cross-layer link? Use Egypt's national grid.
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 GeoJSON file to understand its structure.
0:02
Read
/work/heritage_sites.geojson
1 {
2 "type": "FeatureCollection",
3 "name": "cairo_heritage_sites",
4 "crs": {
5 "type": "name",
6 "properties": {
7 "name": "urn:ogc:def:crs:OGC:1.3:CRS84"
8 }
9 },
10 "features": [
11 {
12 "type": "Feature",
13 "geometry": {
14 "type": "Polygon",
15 "coordinates": [
16 [
17 [
18 31.256,
19 30.0316
20 ],
21 [
22 31.2572,
23 30.0316
24 ],
25 [
26 31.2572,
27 30.0328
28 ],
29 [
30 31.256,
31 30.0328
32 ],
33 [
34 31.256,
35 30.0316
36 ]
37 ]
38 ]
39 },
40 "properties": {
41 "site_id": "EG-CAI-001",
42 "feature_kind": "enclosure",
43 "name_en": "Sultan Hassan Mosque-Madrasa",
44 "name_ar": "مسجد ومدرسة السلطان حسن"
45 }
46 },
47 {
48 "type": "Feature",
49 "geometry": {
50 "type": "LineString",
51 "coordinates": [
52 [
53 31.2556,
54 30.0321
55 ],
56 [
57 31.2566,
58 30.0322
59 ],
60 [
61 31.2576,
62 30.0323
63 ]
64 ]
65 },
66 "properties": {
67 "site_id": "EG-CAI-001",
68 "feature_kind": "axial_line_1",
69 "name_en": "Sultan Hassan Mosque-Madrasa",
70 "name_ar": "مسجد ومدرسة السلطان حسن"
71 }
72 },
73 {
74 "type": "Feature",
75 "geometry": {
76 "type": "LineString",
77 "coordinates": [
78 [
79 31.2567,
80 30.0312
81 ],
82 [
83 31.2566,
84 30.0322
85 ],
86 [
87 31.2565,
88 30.0332
89 ]
90 ]
91 },
92 "properties": {
93 "site_id": "EG-CAI-001",
94 "feature_kind": "axial_line_2",
95 "name_en": "Sultan Hassan Mosque-Madrasa",
96 "name_ar": "مسجد ومدرسة السلطان حسن"
97 }
98 },
99 {
100 "type": "Feature",
101 "geometry": {
102 "type": "Point",
103 "coordinates": [
104 31.2566,
105 30.0326
106 ]
107 },
108 "properties": {
109 "site_id": "EG-CAI-001",
110 "feature_kind": "marker_gateway",
111 "name_en": "Sultan Hassan Mosque-Madrasa",
112 "name_ar": "مسجد ومدرسة السلطان حسن"
113 }
114 },
115 {
116 "type": "Feature",
117 "geometry": {
118 "type": "Point",
119 "coordinates": [
120 31.257,
121 30.0322
122 ]
123 },
124 "properties": {
125 "site_id": "EG-CAI-001",
126 "feature_kind": "marker_mihrab",
127 "name_en": "Sultan Hassan Mosque-Madrasa",
128 "name_ar": "مسجد ومدرسة السلطان حسن"
129 }
130 },
131 {
132 "type": "Feature",
133 "geometry": {
134 "type": "Point",
135 "coordinates": [
136 31.2566,
137 30.0318
138 ]
139 },
140 "properties": {
141 "site_id": "EG-CAI-001",
142 "feature_kind": "marker_minaret",
143 "name_en": "Sultan Hassan Mosque-Madrasa",
144 "name_ar": "مسجد ومدرسة السلطان حسن"
145 }
146 },
147 {
148 "type": "Feature",
149 "geometry": {
150 "type": "MultiPolygon",
151 "coordinates": [
152 [
153 [
154 [
155 31.257,
156 30.0324
157 ],
158 [
159 31.2582,
160 30.0324
161 ],
162 [
163 31.2582,
164 30.0336
165 ],
166 [
167 31.257,
168 30.0336
169 ],
170 [
171 31.257,
172 30.0324
173 ]
174 ]
175 ],
176 [
177 [
178 [
179 31.25865,
180 30.03349
181 ],
182 [
183 31.25935,
184 30.03349
185 ],
186 [
187 31.25935,
188 30.03419
189 ],
190 [
191 31.25865,
192 30.03419
193 ],
194 [
195 31.25865,
196 30.03349
197 ]
198 ]
199 ]
200 ]
201 },
202 "properties": {
203 "site_id": "EG-CAI-002",
204 "feature_kind": "enclosure",
205 "name_en": "Al-Rifai Mosque",
206 "name_ar": "مسجد الرفاعي"
207 }
208 },
209 {
210 "type": "Feature",
211 "geometry": {
212 "type": "LineString",
213 "coordinates": [
214 [
215 31.2566,
216 30.0329
217 ],
218 [
219 31.2576,
220 30.033
221 ],
222 [
223 31.2586,
224 30.0331
225 ]
226 ]
227 },
228 "properties": {
229 "site_id": "EG-CAI-002",
230 "feature_kind": "axial_line_1",
231 "name_en": "Al-Rifai Mosque",
232 "name_ar": "مسجد الرفاعي"
233 }
234 },
235 {
236 "type": "Feature",
237 "geometry": {
238 "type": "Point",
239 "coordinates": [
240 31.2576,
241 30.0334
242 ]
243 },
244 "properties": {
245 "site_id": "EG-CAI-002",
246 "feature_kind": "marker_gateway",
247 "name_en": "Al-Rifai Mosque",
248 "name_ar": "مسجد الرفاعي"
249 }
250 },
251 {
252 "type": "Feature",
253 "geometry": {
254 "type": "Point",
255 "coordinates": [
256 31.258,
257 30.033
258 ]
259 },
260 "properties": {
261 "site_id": "EG-CAI-002",
262 "feature_kind": "marker_mihrab",
263 "name_en": "Al-Rifai Mosque",
264 "name_ar": "مسجد الرفاعي"
265 }
266 },
267 {
268 "type": "Feature",
269 "geometry": {
270 "type": "MultiPolygon",
271 "coordinates": [
272 [
273 [
274 [
275 31.2491,
276 30.0281
277 ],
278 [
279 31.2503,
280 30.0281
281 ],
282 [
283 31.2503,
284 30.0293
285 ],
286 [
287 31.2491,
288 30.0293
289 ],
290 [
291 31.2491,
292 30.0281
293 ]
294 ]
295 ],
296 [
297 [
298 [
299 31.25075,
300 30.02919
301 ],
302 [
303 31.25145,
304 30.02919
305 ],
306 [
307 31.25145,
308 30.02989
309 ],
310 [
311 31.25075,
312 30.02989
313 ],
314 [
315 31.25075,
316 30.02919
317 ]
318 ]
319 ]
320 ]
321 },
322 "properties": {
323 "site_id": "EG-CAI-003",
324 "feature_kind": "enclosure",
325 "name_en": "Ibn Tulun Mosque",
326 "name_ar": "مسجد ابن طولون"
327 }
328 },
329 {
330 "type": "Feature",
331 "geometry": {
332 "type": "LineString",
333 "coordinates": [
334 [
335 31.2487,
336 30.0286
337 ],
338 [
339 31.2497,
340 30.0287
341 ],
342 [
343 31.2507,
344 30.0288
345 ]
346 ]
347 },
348 "properties": {
349 "site_id": "EG-CAI-003",
350 "feature_kind": "axial_line_1",
351 "name_en": "Ibn Tulun Mosque",
352 "name_ar": "مسجد ابن طولون"
353 }
354 },
355 {
356 "type": "Feature",
357 "geometry": {
358 "type": "LineString",
359 "coordinates": [
360 [
361 31.2498,
362 30.0277
363 ],
364 [
365 31.2497,
366 30.0287
367 ],
368 [
369 31.2496,
370 30.0297
371 ]
372 ]
373 },
374 "properties": {
375 "site_id": "EG-CAI-003",
376 "feature_kind": "axial_line_2",
377 "name_en": "Ibn Tulun Mosque",
378 "name_ar": "مسجد ابن طولون"
379 }
380 },
381 {
382 "type": "Feature",
383 "geometry": {
384 "type": "Point",
385 "coordinates": [
386 31.2497,
387 30.0291
388 ]
389 },
390 "properties": {
391 "site_id": "EG-CAI-003",
392 "feature_kind": "marker_gateway",
393 "name_en": "Ibn Tulun Mosque",
394 "name_ar": "مسجد ابن طولون"
395 }
396 },
397 {
398 "type": "Feature",
399 "geometry": {
400 "type": "Point",
401 "coordinates": [
402 31.2501,
403 30.0287
404 ]
405 },
406 "properties": {
407 "site_id": "EG-CAI-003",
408 "feature_kind": "marker_mihrab",
409 "name_en": "Ibn Tulun Mosque",
410 "name_ar": "مسجد ابن طولون"
411 }
412 },
413 {
414 "type": "Feature",
415 "geometry": {
416 "type": "Point",
417 "coordinates": [
418 31.2497,
419 30.0283
420 ]
421 },
422 "properties": {
423 "site_id": "EG-CAI-003",
424 "feature_kind": "marker_minaret",
425 "name_en": "Ibn Tulun Mosque",
426 "name_ar": "مسجد ابن طولون"
427 }
428 },
429 {
430 "type": "Feature",
431 "geometry": {
432 "type": "Polygon",
433 "coordinates": [
434 [
435 [
436 31.2618,
437 30.0453
438 ],
439 [
440 31.263,
441 30.0453
442 ],
443 [
444 31.263,
445 30.0465
446 ],
447 [
448 31.2618,
449 30.0465
450 ],
451 [
452 31.2618,
453 30.0453
454 ]
455 ]
456 ]
457 },
458 "properties": {
459 "site_id": "EG-CAI-004",
460 "feature_kind": "enclosure",
461 "name_en": "Al-Azhar Mosque",
462 "name_ar": "الجامع الأزهر"
463 }
464 },
465 {
466 "type": "Feature",
467 "geometry": {
468 "type": "LineString",
469 "coordinates": [
470 [
471 31.2614,
472 30.0458
473 ],
474 [
475 31.2624,
476 30.0459
477 ],
478 [
479 31.2634,
480 30.046
481 ]
482 ]
483 },
484 "properties": {
485 "site_id": "EG-CAI-004",
486 "feature_kind": "axial_line_1",
487 "name_en": "Al-Azhar Mosque",
488 "name_ar": "الجامع الأزهر"
489 }
490 },
491 {
492 "type": "Feature",
493 "geometry": {
494 "type": "LineString",
495 "coordinates": [
496 [
497 31.2625,
498 30.0449
499 ],
500 [
501 31.2624,
502 30.0459
503 ],
504 [
505 31.2623,
506 30.0469
507 ]
508 ]
509 },
510 "properties": {
511 "site_id": "EG-CAI-004",
512 "feature_kind": "axial_line_2",
513 "name_en": "Al-Azhar Mosque",
514 "name_ar": "الجامع الأزهر"
515 }
516 },
517 {
518 "type": "Feature",
519 "geometry": {
520 "type": "Point",
521 "coordinates": [
522 31.2624,
523 30.0463
524 ]
525 },
526 "properties": {
527 "site_id": "EG-CAI-004",
528 "feature_kind": "marker_gateway",
529 "name_en": "Al-Azhar Mosque",
530 "name_ar": "الجامع الأزهر"
531 }
532 },
533 {
534 "type": "Feature",
535 "geometry": {
536 "type": "Point",
537 "coordinates": [
538 31.2628,
539 30.0459
540 ]
541 },
542 "properties": {
543 "site_id": "EG-CAI-004",
544 "feature_kind": "marker_mihrab",
545 "name_en": "Al-Azhar Mosque",
546 "name_ar": "الجامع الأزهر"
547 }
548 },
549 {
550 "type": "Feature",
551 "geometry": {
552 "type": "Point",
553 "coordinates": [
554 31.2624,
555 30.0455
556 ]
557 },
558 "properties": {
559 "site_id": "EG-CAI-004",
560 "feature_kind": "marker_minaret",
561 "name_en": "Al-Azhar Mosque",
562 "name_ar": "الجامع الأزهر"
563 }
564 },
565 {
566 "type": "Feature",
567 "geometry": {
568 "type": "Polygon",
569 "coordinates": [
570 [
571 [
572 31.262,
573 30.0473
574 ],
575 [
576 31.2632,
577 30.0473
578 ],
579 [
580 31.2632,
581 30.0485
582 ],
583 [
584 31.262,
585 30.0485
586 ],
587 [
588 31.262,
589 30.0473
590 ]
591 ]
592 ]
593 },
594 "properties": {
595 "site_id": "EG-CAI-005",
596 "feature_kind": "enclosure",
597 "name_en": "Al-Hussein Mosque",
598 "name_ar": "مسجد الحسين"
599 }
600 },
601 {
602 "type": "Feature",
603 "geometry": {
604 "type": "LineString",
605 "coordinates": [
606 [
607 31.2616,
608 30.0478
609 ],
610 [
611 31.2626,
612 30.0479
613 ],
614 [
615 31.2636,
616 30.048
617 ]
618 ]
619 },
620 "properties": {
621 "site_id": "EG-CAI-005",
622 "feature_kind": "axial_line_1",
623 "name_en": "Al-Hussein Mosque",
624 "name_ar": "مسجد الحسين"
625 }
626 },
627 {
628 "type": "Feature",
629 "geometry": {
630 "type": "Point",
631 "coordinates": [
632 31.2626,
633 30.0483
634 ]
635 },
636 "properties": {
637 "site_id": "EG-CAI-005",
638 "feature_kind": "marker_gateway",
639 "name_en": "Al-Hussein Mosque",
640 "name_ar": "مسجد الحسين"
641 }
642 },
643 {
644 "type": "Feature",
645 "geometry": {
646 "type": "Point",
647 "coordinates": [
648 31.263,
649 30.0479
650 ]
651 },
652 "properties": {
653 "site_id": "EG-CAI-005",
654 "feature_kind": "marker_mihrab",
655 "name_en": "Al-Hussein Mosque",
656 "name_ar": "مسجد الحسين"
657 }
658 },
659 {
660 "type": "Feature",
661 "geometry": {
662 "type": "MultiPolygon",
663 "coordinates": [
664 [
665 [
666 [
667 31.2614,
668 30.0524
669 ],
670 [
671 31.2626,
672 30.0524
673 ],
674 [
675 31.2626,
676 30.0536
677 ],
678 [
679 31.2614,
680 30.0536
681 ],
682 [
683 31.2614,
684 30.0524
685 ]
686 ]
687 ],
688 [
689 [
690 [
691 31.26305,
692 30.05349
693 ],
694 [
695 31.26375,
696 30.05349
697 ],
698 [
699 31.26375,
700 30.05419
701 ],
702 [
703 31.26305,
704 30.05419
705 ],
706 [
707 31.26305,
708 30.05349
709 ]
710 ]
711 ]
712 ]
713 },
714 "properties": {
715 "site_id": "EG-CAI-006",
716 "feature_kind": "enclosure",
717 "name_en": "Bayt Al-Suhaymi",
718 "name_ar": "بيت السحيمي"
719 }
720 },
721 {
722 "type": "Feature",
723 "geometry": {
724 "type": "LineString",
725 "coordinates": [
726 [
727 31.261,
728 30.0529
729 ],
730 [
731 31.262,
732 30.053
733 ],
734 [
735 31.263,
736 30.0531
737 ]
738 ]
739 },
740 "properties": {
741 "site_id": "EG-CAI-006",
742 "feature_kind": "axial_line_1",
743 "name_en": "Bayt Al-Suhaymi",
744 "name_ar": "بيت السحيمي"
745 }
746 },
747 {
748 "type": "Feature",
749 "geometry": {
750 "type": "Point",
751 "coordinates": [
752 31.262,
753 30.0534
754 ]
755 },
756 "properties": {
757 "site_id": "EG-CAI-006",
758 "feature_kind": "marker_gateway",
759 "name_en": "Bayt Al-Suhaymi",
760 "name_ar": "بيت السحيمي"
761 }
762 },
763 {
764 "type": "Feature",
765 "geometry": {
766 "type": "Point",
767 "coordinates": [
768 31.2624,
769 30.053
770 ]
771 },
772 "properties": {
773 "site_id": "EG-CAI-006",
774 "feature_kind": "marker_mihrab",
775 "name_en": "Bayt Al-Suhaymi",
776 "name_ar": "بيت السحيمي"
777 }
778 },
779 {
780 "type": "Feature",
781 "geometry": {
782 "type": "Point",
783 "coordinates": [
784 31.262,
785 30.0526
786 ]
787 },
788 "properties": {
789 "site_id": "EG-CAI-006",
790 "feature_kind": "marker_minaret",
791 "name_en": "Bayt Al-Suhaymi",
792 "name_ar": "بيت السحيمي"
793 }
794 },
795 {
796 "type": "Feature",
797 "geometry": {
798 "type": "Polygon",
799 "coordinates": [
800 [
801 [
802 31.2574,
803 30.0426
804 ],
805 [
806 31.2586,
807 30.0426
808 ],
809 [
810 31.2586,
811 30.0438
812 ],
813 [
814 31.2574,
815 30.0438
816 ],
817 [
818 31.2574,
819 30.0426
820 ]
821 ]
822 ]
823 },
824 "properties": {
825 "site_id": "EG-CAI-007",
826 "feature_kind": "enclosure",
827 "name_en": "Bab Zuwayla",
828 "name_ar": "باب زويلة"
829 }
830 },
831 {
832 "type": "Feature",
833 "geometry": {
834 "type": "LineString",
835 "coordinates": [
836 [
837 31.257,
838 30.0431
839 ],
840 [
841 31.258,
842 30.0432
843 ],
844 [
845 31.259,
846 30.0433
847 ]
848 ]
849 },
850 "properties": {
851 "site_id": "EG-CAI-007",
852 "feature_kind": "axial_line_1",
853 "name_en": "Bab Zuwayla",
854 "name_ar": "باب زويلة"
855 }
856 },
857 {
858 "type": "Feature",
859 "geometry": {
860 "type": "LineString",
861 "coordinates": [
862 [
863 31.2581,
864 30.0422
865 ],
866 [
867 31.258,
868 30.0432
869 ],
870 [
871 31.2579,
872 30.0442
873 ]
874 ]
875 },
876 "properties": {
877 "site_id": "EG-CAI-007",
878 "feature_kind": "axial_line_2",
879 "name_en": "Bab Zuwayla",
880 "name_ar": "باب زويلة"
881 }
882 },
883 {
884 "type": "Feature",
885 "geometry": {
886 "type": "Point",
887 "coordinates": [
888 31.258,
889 30.0436
890 ]
891 },
892 "properties": {
893 "site_id": "EG-CAI-007",
894 "feature_kind": "marker_gateway",
895 "name_en": "Bab Zuwayla",
896 "name_ar": "باب زويلة"
897 }
898 },
899 {
900 "type": "Feature",
901 "geometry": {
902 "type": "Point",
903 "coordinates": [
904 31.2584,
905 30.0432
906 ]
907 },
908 "properties": {
909 "site_id": "EG-CAI-007",
910 "feature_kind": "marker_mihrab",
911 "name_en": "Bab Zuwayla",
912 "name_ar": "باب زويلة"
913 }
914 },
915 {
916 "type": "Feature",
917 "geometry": {
918 "type": "MultiPolygon",
919 "coordinates": [
920 [
921 [
922 [
923 31.2609,
924 30.0467
925 ],
926 [
927 31.2621,
928 30.0467
929 ],
930 [
931 31.2621,
932 30.0479
933 ],
934 [
935 31.2609,
936 30.0479
937 ],
938 [
939 31.2609,
940 30.0467
941 ]
942 ]
943 ],
944 [
945 [
946 [
947 31.26255,
948 30.04779
949 ],
950 [
951 31.26325,
952 30.04779
953 ],
954 [
955 31.26325,
956 30.04849
957 ],
958 [
959 31.26255,
960 30.04849
961 ],
962 [
963 31.26255,
964 30.04779
965 ]
966 ]
967 ]
968 ]
969 },
970 "properties": {
971 "site_id": "EG-CAI-008",
972 "feature_kind": "enclosure",
973 "name_en": "Khan el-Khalili",
974 "name_ar": "خان الخليلي"
975 }
976 },
977 {
978 "type": "Feature",
979 "geometry": {
980 "type": "LineString",
981 "coordinates": [
982 [
983 31.2605,
984 30.0472
985 ],
986 [
987 31.2615,
988 30.0473
989 ],
990 [
991 31.2625,
992 30.0474
993 ]
994 ]
995 },
996 "properties": {
997 "site_id": "EG-CAI-008",
998 "feature_kind": "axial_line_1",
999 "name_en": "Khan el-Khalili",
1000 "name_ar": "خان الخليلي"
1001 }
1002 },
1003 {
1004 "type": "Feature",
1005 "geometry": {
1006 "type": "LineString",
1007 "coordinates": [
1008 [
1009 31.2616,
1010 30.0463
1011 ],
1012 [
1013 31.2615,
1014 30.0473
1015 ],
1016 [
1017 31.2614,
1018 30.0483
1019 ]
1020 ]
1021 },
1022 "properties": {
1023 "site_id": "EG-CAI-008",
1024 "feature_kind": "axial_line_2",
1025 "name_en": "Khan el-Khalili",
1026 "name_ar": "خان الخليلي"
1027 }
1028 },
1029 {
1030 "type": "Feature",
1031 "geometry": {
1032 "type": "Point",
1033 "coordinates": [
1034 31.2615,
1035 30.0477
1036 ]
1037 },
1038 "properties": {
1039 "site_id": "EG-CAI-008",
1040 "feature_kind": "marker_gateway",
1041 "name_en": "Khan el-Khalili",
1042 "name_ar": "خان الخليلي"
1043 }
1044 },
1045 {
1046 "type": "Feature",
1047 "geometry": {
1048 "type": "Point",
1049 "coordinates": [
1050 31.2619,
1051 30.0473
1052 ]
1053 },
1054 "properties": {
1055 "site_id": "EG-CAI-008",
1056 "feature_kind": "marker_mihrab",
1057 "name_en": "Khan el-Khalili",
1058 "name_ar": "خان الخليلي"
1059 }
1060 },
1061 {
1062 "type": "Feature",
1063 "geometry": {
1064 "type": "Point",
1065 "coordinates": [
1066 31.2615,
1067 30.0469
1068 ]
1069 },
1070 "properties": {
1071 "site_id": "EG-CAI-008",
1072 "feature_kind": "marker_minaret",
1073 "name_en": "Khan el-Khalili",
1074 "name_ar": "خان الخليلي"
1075 }
1076 },
1077 {
1078 "type": "Feature",
1079 "geometry": {
1080 "type": "Polygon",
1081 "coordinates": [
1082 [
1083 [
1084 31.2599,
1085 30.0504
1086 ],
1087 [
1088 31.2611,
1089 30.0504
1090 ],
1091 [
1092 31.2611,
1093 30.0516
1094 ],
1095 [
1096 31.2599,
1097 30.0516
1098 ],
1099 [
1100 31.2599,
1101 30.0504
1102 ]
1103 ]
1104 ]
1105 },
1106 "properties": {
1107 "site_id": "EG-CAI-009",
1108 "feature_kind": "enclosure",
1109 "name_en": "Sabil-Kuttab of Katkhuda",
1110 "name_ar": "سبيل وكتاب عبد الرحمن كتخدا"
1111 }
1112 },
1113 {
1114 "type": "Feature",
1115 "geometry": {
1116 "type": "LineString",
1117 "coordinates": [
1118 [
1119 31.2595,
1120 30.0509
1121 ],
1122 [
1123 31.2605,
1124 30.051
1125 ],
1126 [
1127 31.2615,
1128 30.0511
1129 ]
1130 ]
1131 },
1132 "properties": {
1133 "site_id": "EG-CAI-009",
1134 "feature_kind": "axial_line_1",
1135 "name_en": "Sabil-Kuttab of Katkhuda",
1136 "name_ar": "سبيل وكتاب عبد الرحمن كتخدا"
1137 }
1138 },
1139 {
1140 "type": "Feature",
1141 "geometry": {
1142 "type": "Point",
1143 "coordinates": [
1144 31.2605,
1145 30.0514
1146 ]
1147 },
1148 "properties": {
1149 "site_id": "EG-CAI-009",
1150 "feature_kind": "marker_gateway",
1151 "name_en": "Sabil-Kuttab of Katkhuda",
1152 "name_ar": "سبيل وكتاب عبد الرحمن كتخدا"
1153 }
1154 },
1155 {
1156 "type": "Feature",
1157 "geometry": {
1158 "type": "Point",
1159 "coordinates": [
1160 31.2609,
1161 30.051
1162 ]
1163 },
1164 "properties": {
1165 "site_id": "EG-CAI-009",
1166 "feature_kind": "marker_mihrab",
1167 "name_en": "Sabil-Kuttab of Katkhuda",
1168 "name_ar": "سبيل وكتاب عبد الرحمن كتخدا"
1169 }
1170 },
1171 {
1172 "type": "Feature",
1173 "geometry": {
1174 "type": "MultiPolygon",
1175 "coordinates": [
1176 [
1177 [
1178 [
1179 31.2624,
1180 30.0549
1181 ],
1182 [
1183 31.2636,
1184 30.0549
1185 ],
1186 [
1187 31.2636,
1188 30.0561
1189 ],
1190 [
1191 31.2624,
1192 30.0561
1193 ],
1194 [
1195 31.2624,
1196 30.0549
1197 ]
1198 ]
1199 ],
1200 [
1201 [
1202 [
1203 31.26405,
1204 30.05599
1205 ],
1206 [
1207 31.26475,
1208 30.05599
1209 ],
1210 [
1211 31.26475,
1212 30.05669
1213 ],
1214 [
1215 31.26405,
1216 30.05669
1217 ],
1218 [
1219 31.26405,
1220 30.05599
1221 ]
1222 ]
1223 ]
1224 ]
1225 },
1226 "properties": {
1227 "site_id": "EG-CAI-010",
1228 "feature_kind": "enclosure",
1229 "name_en": "Mosque of Al-Hakim",
1230 "name_ar": "مسجد الحاكم بأمر الله"
1231 }
1232 },
1233 {
1234 "type": "Feature",
1235 "geometry": {
1236 "type": "LineString",
1237 "coordinates": [
1238 [
1239 31.262,
1240 30.0554
1241 ],
1242 [
1243 31.263,
1244 30.0555
1245 ],
1246 [
1247 31.264,
1248 30.0556
1249 ]
1250 ]
1251 },
1252 "properties": {
1253 "site_id": "EG-CAI-010",
1254 "feature_kind": "axial_line_1",
1255 "name_en": "Mosque of Al-Hakim",
1256 "name_ar": "مسجد الحاكم بأمر الله"
1257 }
1258 },
1259 {
1260 "type": "Feature",
1261 "geometry": {
1262 "type": "Point",
1263 "coordinates": [
1264 31.263,
1265 30.0559
1266 ]
1267 },
1268 "properties": {
1269 "site_id": "EG-CAI-010",
1270 "feature_kind": "marker_gateway",
1271 "name_en": "Mosque of Al-Hakim",
1272 "name_ar": "مسجد الحاكم بأمر الله"
1273 }
1274 },
1275 {
1276 "type": "Feature",
1277 "geometry": {
1278 "type": "Point",
1279 "coordinates": [
1280 31.2634,
1281 30.0555
1282 ]
1283 },
1284 "properties": {
1285 "site_id": "EG-CAI-010",
1286 "feature_kind": "marker_mihrab",
1287 "name_en": "Mosque of Al-Hakim",
1288 "name_ar": "مسجد الحاكم بأمر الله"
1289 }
1290 }
1291 ]
1292 }
0:18
Let me understand the task: 1. Read `heritage_sites.geojson` - a GeoJSON with mixed geometry types (Point, LineString, Polygon, MultiPolygon) 2. Create `heritage.gpkg` with three layers: `points`, `lines`, `polygons` 3. Every feature needs to be single-part (no Multi geometries) 4. Keep `site_id` on every feature 5. Use Egypt's national grid (Egypt Red Belt / UTM zone 36N - EPSG:22992? Actually let me check: Egypt's national grid is "Egypt National Grid" - EPSG:7275? Or more commonly, Egypt uses EPSG:22992 (Egypt Red Belt) or EPSG:22993 (Egypt Purple Belt). Actually, "Egypt's national grid" most commonly refers to the "Egypt National Grid" which is EPSG:22992 (Old Egyptian) or EPSG:7275 (newer). Let me think... Actually, looking at this more carefully: - EPSG:22992 = Egypt Red Belt (used for areas 30-33°N) - this is commonly called "Egypt's national grid" - But there's also EPSG:7275 (Egypt National Grid) which is based on ITRF2000 The most common "Egypt's national grid" used in GIS is typically the Egypt Red Belt (EPSG:22992) which is based on Helmert 1906 datum and uses the Modified Mercator projection with a latitude of reference of 30°N. Actually, let me just look up what's commonly known as "Egypt's national grid". It's often EPSG:22992 - "Egypt Red Belt" or EPSG:22993 - "Egypt Purple Belt". But some sources refer to the official one as EPSG:22992. Let me use EPSG:22992 (Egypt Red Belt) as it's the most commonly understood "Egypt national grid". Actually, I think the best approach is to use EPSG:7275 (Egypt National Grid - 2000) which is the newer official grid. Or EPSG:22992 which is older but widely known. Let me just go with EPSG:22992 (Egypt Red Belt) which uses the Modified Mercator projection and is commonly referred to as Egypt's national grid in GIS contexts. Actually, let me reconsider. The most well-known "Egypt national grid" in GIS is: - EPSG:22992 - Egypt Red Belt / Modified Mercator / Helmet 1906 - Covering zone 30-33N But let me just use a commonly accepted one. I'll use EPSG:22992. Wait - actually, it could also be EPSG:7275 (Egypt National Grid - GD / GD Survey of Egypt). Let me just go with the simplest - Egypt Red Belt, EPSG:22992. Now for the script: 1. Read GeoJSON with geopandas 2. Separate into points, lines, polygons 3. Handle Multi-geometries by exploding to single-part 4. Set CRS to EPSG:22992 (or appropriate Egypt national grid) 5. Write each to separate layers in a GPKG Let me write the script. Actually wait - the GeoJSON has CRS84 (WGS84 lat/lon). I need to reproject to Egypt's national grid. Let me use EPSG:22992 (Egypt Red Belt). Let me write the Python script.
0:18
0:18
Write
/work/solve.py
content · 133 lines · py
#!/usr/bin/env python3
"""
Convert heritage_sites.geojson (mixed geometry) into heritage.gpkg
with three single-layer-typed layers: points, lines, polygons.
Reproject from WGS84 (EPSG:4326) to Egypt National Grid (EPSG:22992).
Ensure all geometries are single-part (explode Multi*).
Keep the site_id attribute on every feature.
"""
import geopandas as gpd
from shapely.geometry import (
Point, LineString, Polygon,
MultiPoint, MultiLineString, MultiPolygon
)
import numpy as np
import os
SRC_CRS = "EPSG:4326"
TGT_CRS = "EPSG:22992" # Egypt Red Belt (Egypt's national grid)
SRC_FILE = "/work/heritage_sites.geojson"
OUT_FILE = "/work/heritage.gpkg"
# ---------------------------------------------------------------------------
# 1. Read the GeoJSON
# ---------------------------------------------------------------------------
gdf = gpd.read_file(SRC_FILE)
print(f"Read {len(gdf)} features from GeoJSON.")
print(f"Original CRS: {gdf.crs}")
# Ensure it's in WGS84
if gdf.crs is None:
gdf.set_crs(SRC_CRS, inplace=True)
elif gdf.crs.to_string() != SRC_CRS:
gdf = gdf.to_crs(SRC_CRS)
# ---------------------------------------------------------------------------
# 2. Helper: explode multi-part geometries to single-part
# Keep all properties (including site_id) on each exploded part.
# ---------------------------------------------------------------------------
def explode_to_single(gdf):
"""Explode any Multi* geometries into individual single-part features."""
rows = []
for idx, row in gdf.iterrows():
geom = row.geometry
props = row.drop("geometry").to_dict()
if geom.geom_type.startswith("Multi"):
# Iterate over each component
for part in geom.geoms:
new_row = props.copy()
new_row["geometry"] = part
rows.append(new_row)
else:
# Already single-part
rows.append(props)
result = gpd.GeoDataFrame(rows, crs=gdf.crs, geometry="geometry")
return result
gdf = explode_to_single(gdf)
print(f"After explode: {len(gdf)} features.")
# ---------------------------------------------------------------------------
# 3. Separate by geometry type
# ---------------------------------------------------------------------------
# We only want Point, LineString, Polygon (single-part types)
point_mask = gdf.geometry.type == "Point"
line_mask = gdf.geometry.type == "LineString"
poly_mask = gdf.geometry.type == "Polygon"
gdf_points = gdf[point_mask].copy()
gdf_lines = gdf[line_mask].copy()
gdf_polygons = gdf[poly_mask].copy()
print(f"Points: {len(gdf_points)}")
print(f"Lines: {len(gdf_lines)}")
print(f"Polygons: {len(gdf_polygons)}")
# Warn about any unexpected geometry types
other_mask = ~(point_mask | line_mask | poly_mask)
if other_mask.any():
other_types = gdf[other_mask].geometry.type.unique()
print(f"WARNING: Skipping {other_mask.sum()} features with geometry types: {other_types}")
gdf_other = gdf[other_mask]
for _, row in gdf_other.iterrows():
print(f" Skipped: site_id={row.get('site_id','?')}, geom_type={row.geometry.type}")
# ---------------------------------------------------------------------------
# 4. Reproject to Egypt National Grid
# ---------------------------------------------------------------------------
gdf_points = gdf_points.to_crs(TGT_CRS)
gdf_lines = gdf_lines.to_crs(TGT_CRS)
gdf_polygons = gdf_polygons.to_crs(TGT_CRS)
print(f"Reprojected to {TGT_CRS}.")
# ---------------------------------------------------------------------------
# 5. Write to GPKG – three layers
# ---------------------------------------------------------------------------
if os.path.exists(OUT_FILE):
os.remove(OUT_FILE)
# Write points layer
gdf_points.to_file(OUT_FILE, layer="points", driver="GPKG")
print(f"Wrote {len(gdf_points)} points to layer 'points'.")
# Write lines layer
gdf_lines.to_file(OUT_FILE, layer="lines", driver="GPKG")
print(f"Wrote {len(gdf_lines)} lines to layer 'lines'.")
# Write polygons layer
gdf_polygons.to_file(OUT_FILE, layer="polygons", driver="GPKG")
print(f"Wrote {len(gdf_polygons)} polygons to layer 'polygons'.")
# ---------------------------------------------------------------------------
# 6. Quick verification
# ---------------------------------------------------------------------------
print("\n--- Verification ---")
for layer in ["points", "lines", "polygons"]:
check = gpd.read_file(OUT_FILE, layer=layer)
print(f"Layer '{layer}': {len(check)} features, CRS={check.crs}")
if "site_id" in check.columns:
print(f" site_ids: {check['site_id'].unique()}")
else:
print(f" Columns: {list(check.columns)}")
# Confirm no multi-geometries
multi_types = check.geometry.type[check.geometry.type.str.startswith("Multi")].unique()
if len(multi_types) > 0:
print(f" WARNING: contains multi types: {multi_types}")
else:
print(f" All geometries are single-part. ✓")
print("\nDone.")
Wrote /work/solve.py (5019 chars).
0:24
Now let me run the script.
0:24
Bash
cd /work && python solve.py
Read 50 features from GeoJSON. Original CRS: EPSG:4326 After explode: 55 features. Points: 0 Lines: 0 Polygons: 10 WARNING: Skipping 45 features with geometry types: [None]
Traceback (most recent call last):
File "/work/solve.py", line 86, in <module>
print(f" Skipped: site_id={row.get('site_id','?')}, geom_type={row.geometry.type}")
^^^^^^^^^^^^^^^^^
AttributeError: 'float' object has no attribute 'type'
0:33
The issue is that after the explode, some geometries became `None` (NaN). Let me look at why. The GeoJSON has MultiPolygon features. When I explode them, I need to handle the case where the geometry might be null or invalid. Let me debug by checking the geometry types after explode.
Wait - the issue is that after `explode_to_single`, the function returns a GeoDataFrame but not all rows might have valid geometry. Let me check more carefully.
Actually, the problem is that `gdf.geometry.type` returns NaN for None geometries. Let me check what's happening.
Actually looking at the loop more carefully - I'm iterating with `gdf.iterrows()` and accessing `row.geometry`. For MultiPolygon features, `row.geometry` is a MultiPolygon, so `row.geometry.geoms` should work. But for regular Polygon/Point/LineString, `row.geometry` is just the geometry itself.
Let me add some debugging and fix the logic. Also, when I create the new rows in `explode_to_single`, I'm not setting a proper geometry column. Let me fix this properly.
Actually, the `row.drop("geometry")` returns a Series without geometry. Then I add geometry back as a key in the dict. But when creating a GeoDataFrame, I need to specify the `geometry=` column name.
Let me rewrite the script more carefully.
0:33
Bash
cd /work && python3 -c "
cd /work && python3 -c "
import geopandas as gpd
gdf = gpd.read_file('/work/heritage_sites.geojson')
print('Geometry types:')
print(gdf.geometry.type.value_counts())
print()
for idx, row in gdf.iterrows():
print(f'{idx}: geom_type={row.geometry.geom_type}, props={dict(row.drop(\"geometry\"))}')
"
Geometry types:
Point 25
LineString 15
Polygon 5
MultiPolygon 5
Name: count, dtype: int64
0: geom_type=Polygon, props={'site_id': 'EG-CAI-001', 'feature_kind': 'enclosure', 'name_en': 'Sultan Hassan Mosque-Madrasa', 'name_ar': 'مسجد ومدرسة السلطان حسن'}
1: geom_type=LineString, props={'site_id': 'EG-CAI-001', 'feature_kind': 'axial_line_1', 'name_en': 'Sultan Hassan Mosque-Madrasa', 'name_ar': 'مسجد ومدرسة السلطان حسن'}
2: geom_type=LineString, props={'site_id': 'EG-CAI-001', 'feature_kind': 'axial_line_2', 'name_en': 'Sultan Hassan Mosque-Madrasa', 'name_ar': 'مسجد ومدرسة السلطان حسن'}
3: geom_type=Point, props={'site_id': 'EG-CAI-001', 'feature_kind': 'marker_gateway', 'name_en': 'Sultan Hassan Mosque-Madrasa', 'name_ar': 'مسجد ومدرسة السلطان حسن'}
4: geom_type=Point, props={'site_id': 'EG-CAI-001', 'feature_kind': 'marker_mihrab', 'name_en': 'Sultan Hassan Mosque-Madrasa', 'name_ar': 'مسجد ومدرسة السلطان حسن'}
5: geom_type=Point, props={'site_id': 'EG-CAI-001', 'feature_kind': 'marker_minaret', 'name_en': 'Sultan Hassan Mosque-Madrasa', 'name_ar': 'مسجد ومدرسة السلطان حسن'}
6: geom_type=MultiPolygon, props={'site_id': 'EG-CAI-002', 'feature_kind': 'enclosure', 'name_en': 'Al-Rifai Mosque', 'name_ar': 'مسجد الرفاعي'}
7: geom_type=LineString, props={'site_id': 'EG-CAI-002', 'feature_kind': 'axial_line_1', 'name_en': 'Al-Rifai Mosque', 'name_ar': 'مسجد الرفاعي'}
8: geom_type=Point, props={'site_id': 'EG-CAI-002', 'feature_kind': 'marker_gateway', 'name_en': 'Al-Rifai Mosque', 'name_ar': 'مسجد الرفاعي'}
9: geom_type=Point, props={'site_id': 'EG-CAI-002', 'feature_kind': 'marker_mihrab', 'name_en': 'Al-Rifai Mosque', 'name_ar': 'مسجد الرفاعي'}
10: geom_type=MultiPolygon, props={'site_id': 'EG-CAI-003', 'feature_kind': 'enclosure', 'name_en': 'Ibn Tulun Mosque', 'name_ar': 'مسجد ابن طولون'}
11: geom_type=LineString, props={'site_id': 'EG-CAI-003', 'feature_kind': 'axial_line_1', 'name_en': 'Ibn Tulun Mosque', 'name_ar': 'مسجد ابن طولون'}
12: geom_type=LineString, props={'site_id': 'EG-CAI-003', 'feature_kind': 'axial_line_2', 'name_en': 'Ibn Tulun Mosque', 'name_ar': 'مسجد ابن طولون'}
13: geom_type=Point, props={'site_id': 'EG-CAI-003', 'feature_kind': 'marker_gateway', 'name_en': 'Ibn Tulun Mosque', 'name_ar': 'مسجد ابن طولون'}
14: geom_type=Point, props={'site_id': 'EG-CAI-003', 'feature_kind': 'marker_mihrab', 'name_en': 'Ibn Tulun Mosque', 'name_ar': 'مسجد ابن طولون'}
15: geom_type=Point, props={'site_id': 'EG-CAI-003', 'feature_kind': 'marker_minaret', 'name_en': 'Ibn Tulun Mosque', 'name_ar': 'مسجد ابن طولون'}
16: geom_type=Polygon, props={'site_id': 'EG-CAI-004', 'feature_kind': 'enclosure', 'name_en': 'Al-Azhar Mosque', 'name_ar': 'الجامع الأزهر'}
17: geom_type=LineString, props={'site_id': 'EG-CAI-004', 'feature_kind': 'axial_line_1', 'name_en': 'Al-Azhar Mosque', 'name_ar': 'الجامع الأزهر'}
18: geom_type=LineString, props={'site_id': 'EG-CAI-004', 'feature_kind': 'axial_line_2', 'name_en': 'Al-Azhar Mosque', 'name_ar': 'الجامع الأزهر'}
19: geom_type=Point, props={'site_id': 'EG-CAI-004', 'feature_kind': 'marker_gateway', 'name_en': 'Al-Azhar Mosque', 'name_ar': 'الجامع الأزهر'}
20: geom_type=Point, props={'site_id': 'EG-CAI-004', 'feature_kind': 'marker_mihrab', 'name_en': 'Al-Azhar Mosque', 'name_ar': 'الجامع الأزهر'}
21: geom_type=Point, props={'site_id': 'EG-CAI-004', 'feature_kind': 'marker_minaret', 'name_en': 'Al-Azhar Mosque', 'name_ar': 'الجامع الأزهر'}
22: geom_type=Polygon, props={'site_id': 'EG-CAI-005', 'feature_kind': 'enclosure', 'name_en': 'Al-Hussein Mosque', 'name_ar': 'مسجد الحسين'}
23: geom_type=LineString, props={'site_id': 'EG-CAI-005', 'feature_kind': 'axial_line_1', 'name_en': 'Al-Hussein Mosque', 'name_ar': 'مسجد الحسين'}
24: geom_type=Point, props={'site_id': 'EG-CAI-005', 'feature_kind': 'marker_gateway', 'name_en': 'Al-Hussein Mosque', 'name_ar': 'مسجد الحسين'}
25: geom_type=Point, props={'site_id': 'EG-CAI-005', 'feature_kind': 'marker_mihrab', 'name_en': 'Al-Hussein Mosque', 'name_ar': 'مسجد الحسين'}
26: geom_type=MultiPolygon, props={'site_id': 'EG-CAI-006', 'feature_kind': 'enclosure', 'name_en': 'Bayt Al-Suhaymi', 'name_ar': 'بيت السحيمي'}
27: geom_type=LineString, props={'site_id': 'EG-CAI-006', 'feature_kind': 'axial_line_1', 'name_en': 'Bayt Al-Suhaymi', 'name_ar': 'بيت السحيمي'}
28: geom_type=Point, props={'site_id': 'EG-CAI-006', 'feature_kind': 'marker_gateway', 'name_en': 'Bayt Al-Suhaymi', 'name_ar': 'بيت السحيمي'}
29: geom_type=Point, props={'site_id': 'EG-CAI-006', 'feature_kind': 'marker_mihrab', 'name_en': 'Bayt Al-Suhaymi', 'name_ar': 'بيت السحيمي'}
30: geom_type=Point, props={'site_id': 'EG-CAI-006', 'feature_kind': 'marker_minaret', 'name_en': 'Bayt Al-Suhaymi', 'name_ar': 'بيت السحيمي'}
31: geom_type=Polygon, props={'site_id': 'EG-CAI-007', 'feature_kind': 'enclosure', 'name_en': 'Bab Zuwayla', 'name_ar': 'باب زويلة'}
32: geom_type=LineString, props={'site_id': 'EG-CAI-007', 'feature_kind': 'axial_line_1', 'name_en': 'Bab Zuwayla', 'name_ar': 'باب زويلة'}
33: geom_type=LineString, props={'site_id': 'EG-CAI-007', 'feature_kind': 'axial_line_2', 'name_en': 'Bab Zuwayla', 'name_ar': 'باب زويلة'}
34: geom_type=Point, props={'site_id': 'EG-CAI-007', 'feature_kind': 'marker_gateway', 'name_en': 'Bab Zuwayla', 'name_ar': 'باب زويلة'}
35: geom_type=Point, props={'site_id': 'EG-CAI-007', 'feature_kind': 'marker_mihrab', 'name_en': 'Bab Zuwayla', 'name_ar': 'باب زويلة'}
36: geom_type=MultiPolygon, props={'site_id': 'EG-CAI-008', 'feature_kind': 'enclosure', 'name_en': 'Khan el-Khalili', 'name_ar': 'خان الخليلي'}
37: geom_type=LineString, props={'site_id': 'EG-CAI-008', 'feature_kind': 'axial_line_1', 'name_en': 'Khan el-Khalili', 'name_ar': 'خان الخليلي'}
38: geom_type=LineString, props={'site_id': 'EG-CAI-008', 'feature_kind': 'axial_line_2', 'name_en': 'Khan el-Khalili', 'name_ar': 'خان الخليلي'}
39: geom_type=Point, props={'site_id': 'EG-CAI-008', 'feature_kind': 'marker_gateway', 'name_en': 'Khan el-Khalili', 'name_ar': 'خان الخليلي'}
40: geom_type=Point, props={'site_id': 'EG-CAI-008', 'feature_kind': 'marker_mihrab', 'name_en': 'Khan el-Khalili', 'name_ar': 'خان الخليلي'}
41: geom_type=Point, props={'site_id': 'EG-CAI-008', 'feature_kind': 'marker_minaret', 'name_en': 'Khan el-Khalili', 'name_ar': 'خان الخليلي'}
42: geom_type=Polygon, props={'site_id': 'EG-CAI-009', 'feature_kind': 'enclosure', 'name_en': 'Sabil-Kuttab of Katkhuda', 'name_ar': 'سبيل وكتاب عبد الرحمن كتخدا'}
43: geom_type=LineString, props={'site_id': 'EG-CAI-009', 'feature_kind': 'axial_line_1', 'name_en': 'Sabil-Kuttab of Katkhuda', 'name_ar': 'سبيل وكتاب عبد الرحمن كتخدا'}
44: geom_type=Point, props={'site_id': 'EG-CAI-009', 'feature_kind': 'marker_gateway', 'name_en': 'Sabil-Kuttab of Katkhuda', 'name_ar': 'سبيل وكتاب عبد الرحمن كتخدا'}
45: geom_type=Point, props={'site_id': 'EG-CAI-009', 'feature_kind': 'marker_mihrab', 'name_en': 'Sabil-Kuttab of Katkhuda', 'name_ar': 'سبيل وكتاب عبد الرحمن كتخدا'}
46: geom_type=MultiPolygon, props={'site_id': 'EG-CAI-010', 'feature_kind': 'enclosure', 'name_en': 'Mosque of Al-Hakim', 'name_ar': 'مسجد الحاكم بأمر الله'}
47: geom_type=LineString, props={'site_id': 'EG-CAI-010', 'feature_kind': 'axial_line_1', 'name_en': 'Mosque of Al-Hakim', 'name_ar': 'مسجد الحاكم بأمر الله'}
48: geom_type=Point, props={'site_id': 'EG-CAI-010', 'feature_kind': 'marker_gateway', 'name_en': 'Mosque of Al-Hakim', 'name_ar': 'مسجد الحاكم بأمر الله'}
49: geom_type=Point, props={'site_id': 'EG-CAI-010', 'feature_kind': 'marker_mihrab', 'name_en': 'Mosque of Al-Hakim', 'name_ar': 'مسجد الحاكم بأمر الله'}
0:49
The issue was that after the explode function, the geometry type comparison was wrong. The `geometry.type` was returning float because the explode function wasn't properly preserving geometry. Let me look at the issue more carefully.
The problem is in my `explode_to_single` function. When I iterate and do `row.drop("geometry")`, the geometry column may not be handled correctly. Let me rewrite the script properly.
Actually, the real issue is simpler - the `geometry.type` attribute on a GeoDataFrame returns the type of the geometry column as a string for simple geometries, but after the explode function something went wrong. Let me rewrite more carefully using `geom.geom_type` instead of `geometry.type`.
Wait, actually looking at the error more carefully - the issue is in the `explode_to_single` function. When I do:
```python
for part in geom.geoms:
```
`geom` is a MultiPolygon, and `geom.geoms` returns the individual Polygon parts. But `geom.geom_type` starts with "Multi" so we enter that branch. However, the result rows are dictionaries with "geometry" key... Hmm.
Actually, I think the issue is subtler. Let me rewrite the whole thing more carefully.
0:49
0:49
Write
/work/solve.py
content · 119 lines · py
#!/usr/bin/env python3
"""
Convert heritage_sites.geojson (mixed geometry) into heritage.gpkg
with three single-layer-typed layers: points, lines, polygons.
Reproject from WGS84 (EPSG:4326) to Egypt National Grid (EPSG:22992).
Ensure all geometries are single-part (explode Multi* geometries).
Keep the site_id attribute on every feature (plus all other attributes).
"""
import geopandas as gpd
from shapely.geometry import (
Point, LineString, Polygon,
MultiPoint, MultiLineString, MultiPolygon
)
import os
SRC_CRS = "EPSG:4326"
TGT_CRS = "EPSG:22992" # Egypt Red Belt (Egypt's national grid)
SRC_FILE = "/work/heritage_sites.geojson"
OUT_FILE = "/work/heritage.gpkg"
# ---------------------------------------------------------------------------
# 1. Read
# ---------------------------------------------------------------------------
gdf = gpd.read_file(SRC_FILE)
print(f"Read {len(gdf)} features, CRS={gdf.crs}")
# Ensure CRS set
if gdf.crs is None:
gdf.set_crs(SRC_CRS, inplace=True)
# ---------------------------------------------------------------------------
# 2. Explode multi-part geometries to single-part
# ---------------------------------------------------------------------------
rows = []
for _, feat in gdf.iterrows():
geom = feat.geometry
props = feat.drop("geometry").to_dict()
if geom.geom_type in ("MultiPoint", "MultiLineString", "MultiPolygon"):
for part in geom.geoms:
r = props.copy()
r["geometry"] = part
rows.append(r)
else:
r = props.copy()
r["geometry"] = geom
rows.append(r)
gdf = gpd.GeoDataFrame(rows, crs=gdf.crs)
print(f"After explode: {len(gdf)} features")
# ---------------------------------------------------------------------------
# 3. Split by geometry type
# ---------------------------------------------------------------------------
geom_types = gdf.geometry.apply(lambda g: g.geom_type)
gdf_points = gdf[geom_types == "Point"].copy()
gdf_lines = gdf[geom_types == "LineString"].copy()
gdf_polygons = gdf[geom_types == "Polygon"].copy()
print(f"Points: {len(gdf_points)}")
print(f"Lines: {len(gdf_lines)}")
print(f"Polygons: {len(gdf_polygons)}")
# Check for any other types
for gt in geom_types.unique():
if gt not in ("Point", "LineString", "Polygon"):
count = (geom_types == gt).sum()
print(f"WARNING: {count} features with type '{gt}' will be skipped")
# ---------------------------------------------------------------------------
# 4. Reproject to Egypt National Grid
# ---------------------------------------------------------------------------
gdf_points = gdf_points.to_crs(TGT_CRS)
gdf_lines = gdf_lines.to_crs(TGT_CRS)
gdf_polygons = gdf_polygons.to_crs(TGT_CRS)
print(f"Reprojected to {TGT_CRS}.")
# ---------------------------------------------------------------------------
# 5. Write to GPKG — three layers
# ---------------------------------------------------------------------------
if os.path.exists(OUT_FILE):
os.remove(OUT_FILE)
gdf_points.to_file(OUT_FILE, layer="points", driver="GPKG")
print(f"Wrote layer 'points' with {len(gdf_points)} features.")
gdf_lines.to_file(OUT_FILE, layer="lines", driver="GPKG")
print(f"Wrote layer 'lines' with {len(gdf_lines)} features.")
gdf_polygons.to_file(OUT_FILE, layer="polygons", driver="GPKG")
print(f"Wrote layer 'polygons' with {len(gdf_polygons)} features.")
# ---------------------------------------------------------------------------
# 6. Verification
# ---------------------------------------------------------------------------
print("\n--- Verification ---")
for layer in ["points", "lines", "polygons"]:
check = gpd.read_file(OUT_FILE, layer=layer)
print(f"\nLayer '{layer}':")
print(f" Features: {len(check)}")
print(f" CRS: {check.crs}")
if "site_id" in check.columns:
print(f" site_id values: {list(check['site_id'].unique())}")
else:
print(f" Columns: {list(check.columns)}")
# Verify all single-part
multi_mask = check.geometry.apply(lambda g: g.geom_type.startswith("Multi"))
n_multi = multi_mask.sum()
if n_multi > 0:
print(f" WARNING: {n_multi} multi-part geometries remain!")
else:
print(f" All geometries are single-part. ✓")
# Show a sample
if len(check) > 0:
print(f" Sample site_id: {check.iloc[0]['site_id']}")
print("\nDone.")
Wrote /work/solve.py (4408 chars).
0:54
Let me check if EPSG:22992 is available. That's Egypt Red Belt. Let me verify and run.
0:54
Bash
cd /work && python solve.py
Read 50 features, CRS=EPSG:4326 After explode: 55 features Points: 25 Lines: 15 Polygons: 15 Reprojected to EPSG:22992. Wrote layer 'points' with 25 features. Wrote layer 'lines' with 15 features. Wrote layer 'polygons' with 15 features. --- Verification --- Layer 'points': Features: 25 CRS: EPSG:22992 site_id values: ['EG-CAI-001', 'EG-CAI-002', 'EG-CAI-003', 'EG-CAI-004', 'EG-CAI-005', 'EG-CAI-006', 'EG-CAI-007', 'EG-CAI-008', 'EG-CAI-009', 'EG-CAI-010'] All geometries are single-part. ✓ Sample site_id: EG-CAI-001 Layer 'lines': Features: 15 CRS: EPSG:22992 site_id values: ['EG-CAI-001', 'EG-CAI-002', 'EG-CAI-003', 'EG-CAI-004', 'EG-CAI-005', 'EG-CAI-006', 'EG-CAI-007', 'EG-CAI-008', 'EG-CAI-009', 'EG-CAI-010'] All geometries are single-part. ✓ Sample site_id: EG-CAI-001 Layer 'polygons': Features: 15 CRS: EPSG:22992 site_id values: ['EG-CAI-001', 'EG-CAI-002', 'EG-CAI-003', 'EG-CAI-004', 'EG-CAI-005', 'EG-CAI-006', 'EG-CAI-007', 'EG-CAI-008', 'EG-CAI-009', 'EG-CAI-010'] All geometries are single-part. ✓ Sample site_id: EG-CAI-001 Done.
1:01
The output looks correct. Let me verify the file exists and summarize for the user. - 25 points (from the original 25 Point features) - 15 lines (from the original 15 LineString features) - 15 polygons (5 original Polygon + 5 MultiPolygon exploded into 10 parts = 15 total) All have site_id, all are single-part, all reprojected to EPSG:22992 (Egypt Red Belt / Egypt's national grid).
1:01