source: osm/applications/editors/josm/plugins/smed2/src/s57/S57map.java@ 30231

Last change on this file since 30231 was 30231, checked in by malcolmh, 12 years ago

save

File size: 18.1 KB
Line 
1/* Copyright 2013 Malcolm Herring
2 *
3 * This is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License.
6 *
7 * For a copy of the GNU General Public License, see <http://www.gnu.org/licenses/>.
8 */
9
10package s57;
11
12import java.util.*;
13
14import s57.S57obj;
15import s57.S57obj.*;
16import s57.S57att;
17import s57.S57att.*;
18import s57.S57val;
19import s57.S57val.*;
20
21public class S57map {
22
23 public enum Nflag {
24 ANON, // Edge inner nodes
25 ISOL, // Node not part of Edge
26 CONN, // Edge first and last nodes
27 DPTH // Sounding nodes
28 }
29
30 public class Snode { // All coordinates in map
31 public double lat; // Latitude
32 public double lon; // Longitude
33 public Nflag flg; // Role of node
34
35 public Snode() {
36 flg = Nflag.ANON;
37 lat = 0;
38 lon = 0;
39 }
40 public Snode(double ilat, double ilon) {
41 flg = Nflag.ANON;
42 lat = ilat;
43 lon = ilon;
44 }
45 public Snode(double ilat, double ilon, Nflag iflg) {
46 lat = ilat;
47 lon = ilon;
48 flg = iflg;
49 }
50 }
51
52 public class Dnode extends Snode { // All depth soundings
53 public double val; // Sounding value
54
55 public Dnode() {
56 flg = Nflag.DPTH;
57 lat = 0;
58 lon = 0;
59 val = 0;
60 }
61 public Dnode(double ilat, double ilon, double ival) {
62 flg = Nflag.DPTH;
63 lat = ilat;
64 lon = ilon;
65 val = ival;
66 }
67 }
68
69 public class Edge { // A polyline segment
70 public long first; // First CONN node
71 public long last; // Last CONN node
72 public ArrayList<Long> nodes; // Inner ANON nodes
73
74 public Edge() {
75 first = 0;
76 last = 0;
77 nodes = new ArrayList<Long>();
78 }
79 }
80
81 public enum Rflag {
82 UNKN, AGGR, MASTER, SLAVE, PEER
83 }
84
85 public class Reln {
86 public long id;
87 public Rflag reln;
88 public Reln(long i, Rflag r) {
89 id = i;
90 reln = r;
91 }
92 }
93
94 public class AttMap extends HashMap<Att, AttVal<?>> {
95 public AttMap() {
96 super();
97 }
98 }
99
100 public class RelTab extends ArrayList<Reln> {
101 public RelTab() {
102 super();
103 }
104 }
105
106 public class ObjTab extends HashMap<Integer, AttMap> {
107 public ObjTab() {
108 super();
109 }
110 }
111
112 public class ObjMap extends EnumMap<Obj, ObjTab> {
113 public ObjMap() {
114 super(Obj.class);
115 }
116 }
117
118 public class NodeTab extends HashMap<Long, Snode> {
119 public NodeTab() {
120 super();
121 }
122 }
123
124 public class EdgeTab extends HashMap<Long, Edge> {
125 public EdgeTab() {
126 super();
127 }
128 }
129
130 public class FtrMap extends EnumMap<Obj, ArrayList<Feature>> {
131 public FtrMap() {
132 super(Obj.class);
133 }
134 }
135
136 public class FtrTab extends HashMap<Long, Feature> {
137 public FtrTab() {
138 super();
139 }
140 }
141
142 public class Prim { // Spatial element
143 public long id; // Snode ID for POINTs, Edge ID for LINEs & AREAs)
144 public boolean forward; // Direction of vector used (LINEs & AREAs)
145 public boolean outer; // Exterior/Interior boundary (AREAs)
146 public Prim() {
147 id = 0; forward = true; outer = true;
148 }
149 public Prim(long i) {
150 id = i; forward = true; outer = true;
151 }
152 public Prim(long i, boolean o) {
153 id = i; forward = true; outer = o;
154 }
155 public Prim(long i, boolean f, boolean o) {
156 id = i; forward = f; outer = o;
157 }
158 }
159
160 public class Comp {
161 public long ref;
162 public int size;
163 public Comp(long r, int s) {
164 ref = r;
165 size = s;
166 }
167 }
168
169 public enum Pflag {
170 NOSP, POINT, LINE, AREA
171 }
172
173 public class Geom { // Geometric structure of feature
174 public Pflag prim; // Geometry type
175 public ArrayList<Prim> elems; // Ordered list of elements
176 public int outers; // Number of outers
177 public int inners; // Number of inners
178 public ArrayList<Comp> refs; // Ordered list of compounds
179 public Geom(Pflag p) {
180 prim = p;
181 elems = new ArrayList<Prim>();
182 outers = inners = 0;
183 refs = new ArrayList<Comp>();
184 }
185 }
186
187 public class Feature {
188 public Rflag reln; // Relationship status
189 public Geom geom; // Geometry data
190 public Obj type; // Feature type
191 public AttMap atts; // Feature attributes
192 public RelTab rels; // Related objects
193 public ObjMap objs; // Slave objects
194 public double area; // Area of feature
195 public double length; // Length of feature
196 public Snode centre; // Centre of feature
197
198 Feature() {
199 reln = Rflag.UNKN;
200 geom = new Geom(Pflag.NOSP);
201 type = Obj.C_AGGR;
202 atts = new AttMap();
203 rels = new RelTab();
204 objs = new ObjMap();
205 area = 0;
206 length = 0;
207 centre = new Snode();
208 }
209 }
210
211 public NodeTab nodes;
212 public EdgeTab edges;
213
214 public FtrMap features;
215 public FtrTab index;
216
217 private Feature feature;
218 private Edge edge;
219 private long ref;
220
221 public S57map() {
222 nodes = new NodeTab(); // All nodes in map
223 edges = new EdgeTab(); // All edges in map
224 feature = new Feature(); // Current feature being built
225 features = new FtrMap(); // All features in map, grouped by type
226 index = new FtrTab(); // Feature look-up table
227 ref = 0x0000ffffffff0000L;// Compound reference generator
228 }
229
230 // S57 map building methods
231
232 public void newNode(long id, double lat, double lon, Nflag flag) {
233 nodes.put(id, new Snode(Math.toRadians(lat), Math.toRadians(lon), flag));
234 if (flag == Nflag.ANON) {
235 edge.nodes.add(id);
236 }
237 }
238
239 public void newNode(long id, double lat, double lon, double depth) {
240 nodes.put(id, new Dnode(Math.toRadians(lat), Math.toRadians(lon), depth));
241 }
242
243 public void newFeature(long id, Pflag p, long objl) {
244 feature = new Feature();
245 Obj obj = S57obj.decodeType(objl);
246 if (obj == Obj.C_AGGR) {
247 feature.reln = Rflag.AGGR;
248 }
249 feature.geom = new Geom(p);
250 feature.type = obj;
251 index.put(id, feature);
252 }
253
254 public void newObj(long id, int rind) {
255 Rflag r = Rflag.AGGR;
256 switch (rind) {
257 case 1:
258 r = Rflag.MASTER;
259 break;
260 case 2:
261 r = Rflag.SLAVE;
262 break;
263 case 3:
264 r = Rflag.PEER;
265 break;
266 }
267 feature.rels.add(new Reln(id, r));
268 }
269
270 public void endFeature() {
271
272 }
273
274 public void newAtt(long attl, String atvl) {
275 Att att = S57att.decodeAttribute(attl);
276 AttVal<?> val = S57val.decodeValue(atvl, att);
277 feature.atts.put(att, val);
278 }
279
280 public void newPrim(long id, long ornt, long usag) {
281 feature.geom.elems.add(new Prim(id, (ornt != 2), (usag != 2)));
282 }
283
284 public void addConn(long id, int topi) {
285 if (topi == 1) {
286 edge.first = id;
287 } else {
288 edge.last = id;
289 }
290 }
291
292 public void newEdge(long id) {
293 edge = new Edge();
294 edges.put(id, edge);
295 }
296
297 public void endFile() {
298 for (long id : index.keySet()) {
299 Feature feature = index.get(id);
300 for (Reln rel : feature.rels) {
301 Feature reln = index.get(rel.id);
302 reln.reln = rel.reln;
303 if (feature.reln == Rflag.UNKN) {
304 switch (rel.reln) {
305 case MASTER:
306 feature.reln = Rflag.AGGR;
307 break;
308 case SLAVE:
309 feature.reln = Rflag.MASTER;
310 case PEER:
311 feature.reln = Rflag.PEER;
312 break;
313 default:
314 break;
315 }
316 }
317 ObjTab tab = feature.objs.get(reln.type);
318 if (tab == null) {
319 tab = new ObjTab();
320 feature.objs.put(reln.type, tab);
321 }
322 tab.put(tab.size(), reln.atts);
323 }
324 }
325 for (long id : index.keySet()) {
326 Feature feature = index.get(id);
327 if (feature.reln == Rflag.UNKN) {
328 feature.reln = Rflag.MASTER;
329 }
330 if ((feature.type != Obj.UNKOBJ) && ((feature.reln == Rflag.MASTER) || (feature.reln == Rflag.PEER))) {
331 if (features.get(feature.type) == null) {
332 features.put(feature.type, new ArrayList<Feature>());
333 }
334 features.get(feature.type).add(feature);
335 }
336 }
337 sortGeom();
338 }
339
340 // OSM map building methods
341
342 public void addNode(long id, double lat, double lon) {
343 Snode node = new Snode(Math.toRadians(lat), Math.toRadians(lon));
344 nodes.put(id, node);
345 feature = new Feature();
346 feature.reln = Rflag.AGGR;
347 feature.geom.prim = Pflag.POINT;
348 feature.geom.elems.add(new Prim(id));
349 edge = null;
350 }
351
352 public void addEdge(long id) {
353 feature = new Feature();
354 feature.reln = Rflag.AGGR;
355 feature.geom.prim = Pflag.LINE;
356 feature.geom.elems.add(new Prim(id));
357 edge = new Edge();
358 }
359
360 public void addToEdge(long node) {
361 if (edge.first == 0) {
362 edge.first = node;
363 nodes.get(node).flg = Nflag.CONN;
364 } else {
365 if (edge.last != 0) {
366 edge.nodes.add(edge.last);
367 }
368 edge.last = node;
369 }
370 }
371
372 public void addArea(long id) {
373 feature = new Feature();
374 feature.reln = Rflag.AGGR;
375 feature.geom.prim = Pflag.AREA;
376 feature.geom.elems.add(new Prim(id));
377 edge = null;
378 }
379
380 public void addToArea(long id, boolean outer) {
381 feature.geom.elems.add(new Prim(id, outer));
382 }
383
384 public void addTag(String key, String val) {
385 String subkeys[] = key.split(":");
386 if ((subkeys.length > 1) && subkeys[0].equals("seamark")) {
387 Obj obj = S57obj.enumType(subkeys[1]);
388 if ((subkeys.length > 2) && (obj != Obj.UNKOBJ)) {
389 int idx = 0;
390 Att att = Att.UNKATT;
391 try {
392 idx = Integer.parseInt(subkeys[2]);
393 if (subkeys.length == 4) {
394 att = s57.S57att.enumAttribute(subkeys[3], obj);
395 }
396 } catch (Exception e) {
397 att = S57att.enumAttribute(subkeys[2], obj);
398 }
399 ObjTab items = feature.objs.get(obj);
400 if (items == null) {
401 items = new ObjTab();
402 feature.objs.put(obj, items);
403 }
404 AttMap atts = items.get(idx);
405 if (atts == null) {
406 atts = new AttMap();
407 items.put(idx, atts);
408 }
409 AttVal<?> attval = S57val.convertValue(val, att);
410 if (attval.val != null)
411 atts.put(att, attval);
412 } else {
413 if (subkeys[1].equals("type")) {
414 feature.type = S57obj.enumType(val);
415 if (feature.objs.get(feature.type) == null) {
416 feature.objs.put(feature.type, new ObjTab());
417 }
418 } else {
419 Att att = S57att.enumAttribute(subkeys[1], Obj.UNKOBJ);
420 if (att != Att.UNKATT) {
421 AttVal<?> attval = S57val.convertValue(val, att);
422 if (attval.val != null)
423 feature.atts.put(att, attval);
424 }
425 }
426 }
427 }
428 }
429
430 public void tagsDone(long id) {
431 switch (feature.geom.prim) {
432 case POINT:
433 Snode node = nodes.get(id);
434 if (node.flg != Nflag.CONN) {
435 node.flg = Nflag.ISOL;
436 }
437 feature.length = 0;
438 feature.area = 0;
439 break;
440 case LINE:
441 edges.put(id, edge);
442 nodes.get(edge.first).flg = Nflag.CONN;
443 nodes.get(edge.last).flg = Nflag.CONN;
444 feature.length = calcLength(feature.geom);
445 if (edge.first == edge.last) {
446 feature.geom.prim = Pflag.AREA;
447 feature.area = calcArea(feature.geom);
448 } else {
449 feature.area = 0;
450 }
451 break;
452 case AREA:
453 break;
454 default:
455 break;
456 }
457 if ((feature.type != Obj.UNKOBJ) && !((edge != null) && (edge.last == 0))) {
458 index.put(id, feature);
459 if (features.get(feature.type) == null) {
460 features.put(feature.type, new ArrayList<Feature>());
461 }
462 feature.centre = findCentroid(feature);
463 features.get(feature.type).add(feature);
464 }
465 }
466
467 // Utility methods
468
469 public void sortGeom() {
470 for (long id : index.keySet()) {
471 feature = index.get(id);
472 Geom geom = feature.geom;
473 Geom sort = new Geom(geom.prim);
474 long first = 0;
475 long last = 0;
476 Comp comp = null;
477 boolean next = true;
478 if ((geom.prim == Pflag.LINE) || (geom.prim == Pflag.AREA)) {
479 int sweep = geom.elems.size();
480 while (!geom.elems.isEmpty()) {
481 Prim prim = geom.elems.remove(0);
482 Edge edge = edges.get(prim.id);
483 if (next == true) {
484 next = false;
485 if (prim.forward) {
486 first = edge.first;
487 last = edge.last;
488 } else {
489 first = edge.last;
490 last = edge.first;
491 }
492 sort.elems.add(prim);
493 if (prim.outer) {
494 sort.outers++;
495 } else {
496 sort.inners++;
497 }
498 comp = new Comp(ref++, 1);
499 sort.refs.add(comp);
500 } else {
501 if (prim.forward) {
502 if (edge.first == last) {
503 sort.elems.add(prim);
504 last = edge.last;
505 comp.size++;
506 } else if (edge.last == first) {
507 sort.elems.add(0, prim);
508 first = edge.first;
509 comp.size++;
510 } else {
511 geom.elems.add(prim);
512 }
513 } else {
514 if (edge.last == last) {
515 sort.elems.add(prim);
516 last = edge.first;
517 comp.size++;
518 } else if (edge.first == first) {
519 sort.elems.add(0, prim);
520 first = edge.last;
521 comp.size++;
522 } else {
523 geom.elems.add(prim);
524 }
525 }
526 }
527 if (--sweep == 0) {
528 next = true;
529 sweep = geom.elems.size();
530 }
531 }
532 feature.geom = sort;
533 }
534 if (geom.prim == Pflag.AREA) {
535 ArrayList<Prim> outers = new ArrayList<Prim>();
536 ArrayList<Prim> inners = new ArrayList<Prim>();
537 for (Prim prim : feature.geom.elems) {
538 if (prim.outer) {
539 outers.add(prim);
540 } else {
541 inners.add(prim);
542 }
543 }
544 ArrayList<Prim> sorting = outers;
545 ArrayList<Prim> closed = null;
546 sort = new Geom(geom.prim);
547 sort.outers = feature.geom.outers;
548 sort.inners = feature.geom.inners;
549 sort.refs = feature.geom.refs;
550 next = true;
551 while (!sorting.isEmpty()) {
552 Prim prim = sorting.remove(0);
553 Edge edge = edges.get(prim.id);
554 if (next == true) {
555 next = false;
556 closed = new ArrayList<Prim>();
557 closed.add(prim);
558 if (prim.forward) {
559 first = edge.first;
560 last = edge.last;
561 } else {
562 first = edge.last;
563 last = edge.first;
564 }
565 } else {
566 if (prim.forward) {
567 if (edge.first == last) {
568 last = edge.last;
569 closed.add(prim);
570 } else {
571 sorting.add(0, prim);
572 next = true;
573 }
574 } else {
575 if (edge.last == last) {
576 last = edge.first;
577 closed.add(prim);
578 } else {
579 sorting.add(0, prim);
580 next = true;
581 }
582 }
583 }
584 if (first == last) {
585 sort.elems.addAll(closed);
586 next = true;
587 }
588 if (sorting.isEmpty() && sorting == outers) {
589 sorting = inners;
590 next = true;
591 }
592 }
593 feature.geom = sort;
594 }
595 }
596 }
597
598 public class EdgeIterator {
599 Edge edge;
600 boolean forward;
601 ListIterator<Long> it;
602
603 public EdgeIterator(Edge e, boolean dir) {
604 edge = e;
605 forward = dir;
606 it = null;
607 }
608
609 public boolean hasNext() {
610 return (edge != null);
611 }
612
613 public long nextRef() {
614 long ref = 0;
615 if (forward) {
616 if (it == null) {
617 ref = edge.first;
618 it = edge.nodes.listIterator();
619 } else {
620 if (it.hasNext()) {
621 ref = it.next();
622 } else {
623 ref = edge.last;
624 edge = null;
625 }
626 }
627 } else {
628 if (it == null) {
629 ref = edge.last;
630 it = edge.nodes.listIterator(edge.nodes.size());
631 } else {
632 if (it.hasPrevious()) {
633 ref = it.previous();
634 } else {
635 ref = edge.first;
636 edge = null;
637 }
638 }
639 }
640 return ref;
641 }
642
643 public Snode next() {
644 return nodes.get(nextRef());
645 }
646 }
647
648 public class GeomIterator {
649 Geom geom;
650 Prim prim;
651 EdgeIterator eit;
652 ListIterator<S57map.Prim> it;
653 int cc, ec;
654 Comp comp;
655
656 public GeomIterator(Geom g) {
657 geom = g;
658 eit = null;
659 cc = ec = 0;
660 comp = null;
661 if ((geom.prim != Pflag.NOSP) && (geom.prim != Pflag.POINT)) {
662 it = geom.elems.listIterator();
663 } else {
664 it = null;
665 }
666 }
667
668 public boolean hasMore() {
669 return (cc < geom.refs.size());
670 }
671
672 public long getMore() {
673 comp = geom.refs.get(cc++);
674 ec = 0;
675 return comp.ref;
676 }
677
678 public boolean hasNext() {
679 if (eit == null) {
680 return (ec < comp.size);
681 } else {
682 return (eit.hasNext());
683 }
684 }
685
686 public long nextRef() {
687 if ((eit == null) && (ec < comp.size)) {
688 prim = geom.elems.get(ec++);
689 eit = new EdgeIterator(edges.get(prim.id), prim.forward);
690 }
691 long ref = eit.nextRef();
692 if (!eit.hasNext()) {
693 eit = null;
694 }
695 return ref;
696 }
697
698 public Snode next() {
699 return nodes.get(nextRef());
700 }
701 }
702
703 double signedArea(Geom geom) {
704 Snode node;
705 double lat, lon, llon, llat;
706 lat = lon = llon = llat = 0;
707 double sigma = 0;
708 GeomIterator it = new GeomIterator(geom);
709 it.getMore();
710 while (it.hasNext()) {
711 llon = lon;
712 llat = lat;
713 node = it.next();
714 lat = node.lat;
715 lon = node.lon;
716 sigma += (lon * Math.sin(llat)) - (llon * Math.sin(lat));
717 }
718 return sigma / 2.0;
719 }
720
721 public boolean handOfArea(Geom geom) {
722 return (signedArea(geom) < 0);
723 }
724
725 public double calcArea(Geom geom) {
726 return Math.abs(signedArea(geom)) * 3444 * 3444;
727 }
728
729 public double calcLength(Geom geom) {
730 Snode node;
731 double lat, lon, llon, llat;
732 lat = lon = llon = llat = 0;
733 double sigma = 0;
734 GeomIterator it = new GeomIterator(geom);
735 it.getMore();
736 if (it.hasNext()) {
737 node = it.next();
738 lat = node.lat;
739 lon = node.lon;
740 while (it.hasNext()) {
741 llon = lon;
742 llat = lat;
743 node = it.next();
744 lat = node.lat;
745 lon = node.lon;
746 sigma += Math.acos(Math.sin(lat) * Math.sin(llat) + Math.cos(lat) * Math.cos(llat) * Math.cos(llon - lon));
747 }
748 }
749 return sigma * 3444;
750 }
751
752 public Snode findCentroid(Feature feature) {
753 double lat, lon, slat, slon, llat, llon;
754 llat = llon = lat = lon = slat = slon = 0;
755 double sarc = 0;
756 boolean first = true;
757 switch (feature.geom.prim) {
758 case POINT:
759 return nodes.get(feature.geom.elems.get(0).id);
760 case LINE:
761 GeomIterator it = new GeomIterator(feature.geom);
762 it.getMore();
763 while (it.hasNext()) {
764 Snode node = it.next();
765 lat = node.lat;
766 lon = node.lon;
767 if (first) {
768 first = false;
769 } else {
770 sarc += (Math.acos(Math.cos(lon - llon) * Math.cos(lat - llat)));
771 }
772 llat = lat;
773 llon = lon;
774 }
775 double harc = sarc / 2;
776 sarc = 0;
777 first = true;
778 it = new GeomIterator(feature.geom);
779 while (it.hasNext()) {
780 it.getMore();
781 Snode node = it.next();
782 lat = node.lat;
783 lon = node.lon;
784 if (first) {
785 first = false;
786 } else {
787 sarc = (Math.acos(Math.cos(lon - llon) * Math.cos(lat - llat)));
788 if (sarc > harc)
789 break;
790 }
791 harc -= sarc;
792 llat = lat;
793 llon = lon;
794 }
795 return new Snode(llat + ((lat - llat) * harc / sarc), llon + ((lon - llon) * harc / sarc));
796 case AREA:
797 GeomIterator bit = new GeomIterator(feature.geom);
798 bit.getMore();
799 while (bit.hasNext()) {
800 Snode node = bit.next();
801 lat = node.lat;
802 lon = node.lon;
803 if (first) {
804 first = false;
805 } else {
806 double arc = (Math.acos(Math.cos(lon - llon) * Math.cos(lat - llat)));
807 slat += ((lat + llat) / 2 * arc);
808 slon += ((lon + llon) / 2 * arc);
809 sarc += arc;
810 }
811 llon = lon;
812 llat = lat;
813 }
814 return new Snode((sarc > 0.0 ? slat / sarc : 0.0), (sarc > 0.0 ? slon / sarc : 0.0));
815 default:
816 }
817 return null;
818 }
819
820}
Note: See TracBrowser for help on using the repository browser.