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

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

save

File size: 16.8 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
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 RelTab extends ArrayList<Reln> {
95 public RelTab() {
96 super();
97 }
98 }
99
100 public class ObjTab extends HashMap<Integer, AttMap> {
101 public ObjTab() {
102 super();
103 }
104 }
105
106 public class ObjMap extends EnumMap<Obj, ObjTab> {
107 public ObjMap() {
108 super(Obj.class);
109 }
110 }
111
112 public class Aggr {
113 public RelTab rels;
114 public long par;
115 public Aggr() {
116 rels = new RelTab();
117 par = 0;
118 }
119 }
120
121 public class AttMap extends HashMap<Att, AttVal<?>> {
122 public AttMap() {
123 super();
124 }
125 }
126
127 public class NodeTab extends HashMap<Long, Snode> {
128 public NodeTab() {
129 super();
130 }
131 }
132
133 public class EdgeTab extends HashMap<Long, Edge> {
134 public EdgeTab() {
135 super();
136 }
137 }
138
139 public class FtrMap extends EnumMap<Obj, ArrayList<Feature>> {
140 public FtrMap() {
141 super(Obj.class);
142 }
143 }
144
145 public class FtrTab extends HashMap<Long, Feature> {
146 public FtrTab() {
147 super();
148 }
149 }
150
151 public class Prim { // Spatial element
152 public long id; // Snode ID for POINTs, Edge ID for LINEs & AREAs)
153 public boolean forward; // Direction of vector used (LINEs & AREAs)
154 public boolean outer; // Exterior/Interior boundary (AREAs)
155 public Prim() {
156 id = 0; forward = true; outer = true;
157 }
158 public Prim(long i) {
159 id = i; forward = true; outer = true;
160 }
161 public Prim(long i, boolean o) {
162 id = i; forward = true; outer = o;
163 }
164 public Prim(long i, boolean f, boolean o) {
165 id = i; forward = f; outer = o;
166 }
167 }
168
169 public class Comp {
170 public long ref;
171 public int size;
172 public Comp(long r, int s) {
173 ref = r;
174 size = s;
175 }
176 }
177
178 public enum Pflag {
179 NOSP, POINT, LINE, AREA
180 }
181
182 public class Geom { // Geometric structure of feature
183 public Pflag prim; // Geometry type
184 public ArrayList<Prim> elems; // Ordered list of elements
185 public int outers; // Number of outers
186 public int inners; // Number of inners
187 public ArrayList<Comp> refs; // Ordered list of compounds
188 public double area; // Area of feature
189 public double length; // Length of feature
190 public Snode centre; // Centre of feature
191 public Geom(Pflag p) {
192 prim = p;
193 elems = new ArrayList<Prim>();
194 outers = inners = 0;
195 refs = new ArrayList<Comp>();
196 area = 0;
197 length = 0;
198 centre = new Snode();
199 }
200 }
201
202 public class Feature {
203 public Rflag reln; // Relationship status
204 public Geom geom; // Geometry data
205 public Obj type; // Feature type
206 public AttMap atts; // Feature attributes
207 public Aggr aggr; // Related objects
208 public ObjMap objs; // Slave object attributes
209
210 Feature() {
211 reln = Rflag.UNKN;
212 geom = new Geom(Pflag.NOSP);
213 type = Obj.C_AGGR;
214 atts = new AttMap();
215 aggr = new Aggr();
216 objs = new ObjMap();
217 }
218 }
219
220 public NodeTab nodes;
221 public EdgeTab edges;
222
223 public FtrMap features;
224 public FtrTab index;
225
226 public long ref;
227 private Feature feature;
228 private Edge edge;
229
230 public S57map() {
231 nodes = new NodeTab(); // All nodes in map
232 edges = new EdgeTab(); // All edges in map
233 feature = new Feature(); // Current feature being built
234 features = new FtrMap(); // All features in map, grouped by type
235 index = new FtrTab(); // Feature look-up table
236 ref = 0x0000ffffffff0000L;// Compound reference generator
237 }
238
239 // S57 map building methods
240
241 public void newNode(long id, double lat, double lon, Nflag flag) {
242 nodes.put(id, new Snode(Math.toRadians(lat), Math.toRadians(lon), flag));
243 if (flag == Nflag.ANON) {
244 edge.nodes.add(id);
245 }
246 }
247
248 public void newNode(long id, double lat, double lon, double depth) {
249 nodes.put(id, new Dnode(Math.toRadians(lat), Math.toRadians(lon), depth));
250 }
251
252 public void newFeature(long id, Pflag p, long objl) {
253 feature = new Feature();
254 Obj obj = S57obj.decodeType(objl);
255 if (obj == Obj.BCNWTW)
256 obj = Obj.BCNLAT;
257 if (obj == Obj.BOYWTW)
258 obj = Obj.BOYLAT;
259 if (obj == Obj.C_AGGR)
260 feature.reln = Rflag.AGGR;
261 feature.geom = new Geom(p);
262 feature.type = obj;
263 if (obj != Obj.UNKOBJ) {
264 index.put(id, feature);
265 }
266 }
267
268 public void newObj(long id, int rind) {
269 Rflag r = Rflag.AGGR;
270 switch (rind) {
271 case 1:
272 r = Rflag.MASTER;
273 break;
274 case 2:
275 r = Rflag.SLAVE;
276 break;
277 case 3:
278 r = Rflag.UNKN;
279 break;
280 }
281 feature.aggr.rels.add(new Reln(id, r));
282 }
283
284 public void endFeature() {
285
286 }
287
288 public void newAtt(long attl, String atvl) {
289 Att att = S57att.decodeAttribute(attl);
290 AttVal<?> val = S57val.decodeValue(atvl, att);
291 feature.atts.put(att, val);
292 }
293
294 public void newPrim(long id, long ornt, long usag) {
295 feature.geom.elems.add(new Prim(id, (ornt != 2), (usag != 2)));
296 }
297
298 public void addConn(long id, int topi) {
299 if (topi == 1) {
300 edge.first = id;
301 } else {
302 edge.last = id;
303 }
304 }
305
306 public void newEdge(long id) {
307 edge = new Edge();
308 edges.put(id, edge);
309 }
310
311 public void endFile() {
312 for (long id : index.keySet()) {
313 Feature feature = index.get(id);
314 if ((feature.geom.prim == Pflag.LINE) || (feature.geom.prim == Pflag.AREA)) {
315 feature.geom.outers = 1;
316 feature.geom.inners = 0;
317 feature.geom.refs = new ArrayList<Comp>();
318 Comp comp = new Comp(ref++, 0);
319 feature.geom.refs.add(comp);
320 ListIterator<S57map.Prim> ite = feature.geom.elems.listIterator();
321 long first = 0;
322 while (ite.hasNext()) {
323 Prim prim = ite.next();
324 Edge edge = edges.get(prim.id);
325 if (!prim.outer) {
326 if (first == 0) {
327 feature.geom.inners++;
328 comp = new Comp(ref++, 0);
329 feature.geom.refs.add(comp);
330 first = edge.first;
331 } else {
332 if (edge.last == first) {
333 first = 0;
334 }
335 }
336 }
337 comp.size++;
338 }
339 }
340 }
341 for (long id : index.keySet()) {
342 Feature feature = index.get(id);
343 for (Reln reln : feature.aggr.rels) {
344 Feature rel = index.get(reln.id);
345 if (cmpGeoms(feature.geom, rel.geom)) {
346 switch (reln.reln) {
347 case MASTER:
348 feature.reln = Rflag.AGGR;
349 break;
350 case SLAVE:
351 feature.reln = Rflag.MASTER;
352 break;
353 default:
354 feature.reln = Rflag.UNKN;
355 break;
356 }
357 rel.reln = reln.reln;
358 } else {
359 reln.reln = Rflag.UNKN;
360 }
361 }
362 }
363 for (long id : index.keySet()) {
364 Feature feature = index.get(id);
365 if (feature.reln == Rflag.UNKN) {
366 feature.reln = Rflag.MASTER;
367 }
368 if ((feature.type != Obj.UNKOBJ) && (feature.reln == Rflag.MASTER)) {
369 if (features.get(feature.type) == null) {
370 features.put(feature.type, new ArrayList<Feature>());
371 }
372 features.get(feature.type).add(feature);
373 }
374 }
375 for (long id : index.keySet()) {
376 Feature feature = index.get(id);
377 for (Reln reln : feature.aggr.rels) {
378 Feature rel = index.get(reln.id);
379 if (rel.reln == Rflag.SLAVE) {
380 if (feature.objs.get(rel.type) == null) {
381 feature.objs.put(rel.type, new ObjTab());
382 }
383 ObjTab tab = feature.objs.get(rel.type);
384 int ix = tab.size();
385 tab.put(ix, rel.atts);
386 }
387 }
388 }
389 }
390
391 // OSM map building methods
392
393 public void addNode(long id, double lat, double lon) {
394 Snode node = new Snode(Math.toRadians(lat), Math.toRadians(lon));
395 nodes.put(id, node);
396 feature = new Feature();
397 feature.reln = Rflag.AGGR;
398 feature.geom.prim = Pflag.POINT;
399 feature.geom.elems.add(new Prim(id));
400 edge = null;
401 }
402
403 public void addEdge(long id) {
404 feature = new Feature();
405 feature.reln = Rflag.AGGR;
406 feature.geom.prim = Pflag.LINE;
407 feature.geom.elems.add(new Prim(id));
408 edge = new Edge();
409 }
410
411 public void addToEdge(long node) {
412 if (edge.first == 0) {
413 edge.first = node;
414 nodes.get(node).flg = Nflag.CONN;
415 } else {
416 if (edge.last != 0) {
417 edge.nodes.add(edge.last);
418 }
419 edge.last = node;
420 }
421 }
422
423 public void addArea(long id) {
424 feature = new Feature();
425 feature.reln = Rflag.AGGR;
426 feature.geom.prim = Pflag.AREA;
427 feature.geom.elems.add(new Prim(id));
428 edge = null;
429 }
430
431 public void addToArea(long id, boolean outer) {
432 feature.geom.elems.add(new Prim(id, outer));
433 }
434
435 public void addTag(String key, String val) {
436 String subkeys[] = key.split(":");
437 if ((subkeys.length > 1) && subkeys[0].equals("seamark")) {
438 Obj obj = S57obj.enumType(subkeys[1]);
439 if ((subkeys.length > 2) && (obj != Obj.UNKOBJ)) {
440 int idx = 0;
441 Att att = Att.UNKATT;
442 try {
443 idx = Integer.parseInt(subkeys[2]);
444 if (subkeys.length == 4) {
445 att = s57.S57att.enumAttribute(subkeys[3], obj);
446 }
447 } catch (Exception e) {
448 att = S57att.enumAttribute(subkeys[2], obj);
449 }
450 ObjTab items = feature.objs.get(obj);
451 if (items == null) {
452 items = new ObjTab();
453 feature.objs.put(obj, items);
454 Feature type = new Feature();
455 type.reln = Rflag.SLAVE;
456 type.type = obj;
457 type.geom = feature.geom;
458 }
459// AttMap atts = items.get(idx);
460// if (atts == null) {
461// atts = new AttMap();
462// items.put(idx, atts);
463// }
464// AttVal<?> attval = S57val.convertValue(val, att);
465// if (attval.val != null)
466// atts.put(att, attval);
467 } else {
468 if (subkeys[1].equals("type")) {
469 obj = S57obj.enumType(val);
470 if (feature.objs.get(feature.type) == null) {
471 feature.objs.put(feature.type, new ObjTab());
472 Feature type = new Feature();
473 type.reln = Rflag.MASTER;
474 type.type = obj;
475 type.geom = feature.geom;
476 }
477 } else {
478 Att att = S57att.enumAttribute(subkeys[1], Obj.UNKOBJ);
479 if (att != Att.UNKATT) {
480 AttVal<?> attval = S57val.convertValue(val, att);
481 if (attval.val != null)
482 feature.atts.put(att, attval);
483 }
484 }
485 }
486 }
487 }
488
489 public void tagsDone(long id) {
490 switch (feature.geom.prim) {
491 case POINT:
492 Snode node = nodes.get(id);
493 if (node.flg != Nflag.CONN) {
494 node.flg = Nflag.ISOL;
495 }
496 feature.geom.length = 0;
497 feature.geom.area = 0;
498 break;
499 case LINE:
500 edges.put(id, edge);
501 nodes.get(edge.first).flg = Nflag.CONN;
502 nodes.get(edge.last).flg = Nflag.CONN;
503 feature.geom.length = calcLength(feature.geom);
504 if (edge.first == edge.last) {
505 feature.geom.prim = Pflag.AREA;
506 feature.geom.area = calcArea(feature.geom);
507 } else {
508 feature.geom.area = 0;
509 }
510 break;
511 case AREA:
512 break;
513 default:
514 break;
515 }
516 if ((feature.type != Obj.UNKOBJ) && !((edge != null) && (edge.last == 0))) {
517 index.put(id, feature);
518 if (features.get(feature.type) == null) {
519 features.put(feature.type, new ArrayList<Feature>());
520 }
521 features.get(feature.type).add(feature);
522 feature.geom.centre = findCentroid(feature);
523 }
524 }
525
526 // Utility methods
527
528 public boolean cmpGeoms (Geom g1, Geom g2) {
529 return ((g1.prim == g2.prim) && (g1.outers == g2.outers) && (g1.inners == g2.inners) && (g1.elems.size() == g2.elems.size()));
530 }
531
532 public class EdgeIterator {
533 Edge edge;
534 boolean forward;
535 ListIterator<Long> it;
536
537 public EdgeIterator(Edge e, boolean dir) {
538 edge = e;
539 forward = dir;
540 it = null;
541 }
542
543 public boolean hasNext() {
544 return (edge != null);
545 }
546
547 public long nextRef() {
548 long ref = 0;
549 if (forward) {
550 if (it == null) {
551 ref = edge.first;
552 it = edge.nodes.listIterator();
553 } else {
554 if (it.hasNext()) {
555 ref = it.next();
556 } else {
557 ref = edge.last;
558 edge = null;
559 }
560 }
561 } else {
562 if (it == null) {
563 ref = edge.last;
564 it = edge.nodes.listIterator(edge.nodes.size());
565 } else {
566 if (it.hasPrevious()) {
567 ref = it.previous();
568 } else {
569 ref = edge.first;
570 edge = null;
571 }
572 }
573 }
574 return ref;
575 }
576
577 public Snode next() {
578 return nodes.get(nextRef());
579 }
580 }
581
582 public class GeomIterator {
583 Geom geom;
584 Prim prim;
585 EdgeIterator eit;
586 ListIterator<S57map.Prim> ite;
587 ListIterator<Comp> itc;
588 Comp comp;
589 int ec;
590 long lastref;
591
592 public GeomIterator(Geom g) {
593 geom = g;
594 lastref = 0;
595 ite = geom.elems.listIterator();
596 itc = geom.refs.listIterator();
597 }
598
599 public boolean hasComp() {
600 return (itc.hasNext());
601 }
602
603 public long nextComp() {
604 comp = itc.next();
605 ec = comp.size;
606 lastref = 0;
607 return comp.ref;
608 }
609
610 public boolean hasEdge() {
611 return (ec > 0) && ite.hasNext();
612 }
613
614 public long nextEdge() {
615 prim = ite.next();
616 eit = new EdgeIterator(edges.get(prim.id), prim.forward);
617 ec--;
618 return prim.id;
619 }
620
621 public boolean hasNode() {
622 return (eit.hasNext());
623 }
624
625 public long nextRef(boolean all) {
626 long ref = eit.nextRef();
627 if (!all && (ref == lastref)) {
628 ref = eit.nextRef();
629 }
630 lastref = ref;
631 return ref;
632 }
633
634 public long nextRef() {
635 return nextRef(false);
636 }
637
638 public Snode next() {
639 return nodes.get(nextRef());
640 }
641 }
642
643 double signedArea(Geom geom) {
644 Snode node;
645 double lat, lon, llon, llat;
646 lat = lon = llon = llat = 0;
647 double sigma = 0;
648 GeomIterator it = new GeomIterator(geom);
649 it.nextComp();
650 while (it.hasNode()) {
651 llon = lon;
652 llat = lat;
653 node = it.next();
654 lat = node.lat;
655 lon = node.lon;
656 sigma += (lon * Math.sin(llat)) - (llon * Math.sin(lat));
657 }
658 return sigma / 2.0;
659 }
660
661 public boolean handOfArea(Geom geom) {
662 return (signedArea(geom) < 0);
663 }
664
665 public double calcArea(Geom geom) {
666 return Math.abs(signedArea(geom)) * 3444 * 3444;
667 }
668
669 public double calcLength(Geom geom) {
670 Snode node;
671 double lat, lon, llon, llat;
672 lat = lon = llon = llat = 0;
673 double sigma = 0;
674 GeomIterator it = new GeomIterator(geom);
675 it.nextComp();
676 if (it.hasNode()) {
677 node = it.next();
678 lat = node.lat;
679 lon = node.lon;
680 while (it.hasNode()) {
681 llon = lon;
682 llat = lat;
683 node = it.next();
684 lat = node.lat;
685 lon = node.lon;
686 sigma += Math.acos(Math.sin(lat) * Math.sin(llat) + Math.cos(lat) * Math.cos(llat) * Math.cos(llon - lon));
687 }
688 }
689 return sigma * 3444;
690 }
691
692 public Snode findCentroid(Feature feature) {
693 double lat, lon, slat, slon, llat, llon;
694 llat = llon = lat = lon = slat = slon = 0;
695 double sarc = 0;
696 boolean first = true;
697 switch (feature.geom.prim) {
698 case POINT:
699 return nodes.get(feature.geom.elems.get(0).id);
700 case LINE:
701 GeomIterator it = new GeomIterator(feature.geom);
702 it.nextComp();
703 while (it.hasNode()) {
704 Snode node = it.next();
705 lat = node.lat;
706 lon = node.lon;
707 if (first) {
708 first = false;
709 } else {
710 sarc += (Math.acos(Math.cos(lon - llon) * Math.cos(lat - llat)));
711 }
712 llat = lat;
713 llon = lon;
714 }
715 double harc = sarc / 2;
716 sarc = 0;
717 first = true;
718 it = new GeomIterator(feature.geom);
719 while (it.hasNode()) {
720 it.nextComp();
721 Snode node = it.next();
722 lat = node.lat;
723 lon = node.lon;
724 if (first) {
725 first = false;
726 } else {
727 sarc = (Math.acos(Math.cos(lon - llon) * Math.cos(lat - llat)));
728 if (sarc > harc)
729 break;
730 }
731 harc -= sarc;
732 llat = lat;
733 llon = lon;
734 }
735 return new Snode(llat + ((lat - llat) * harc / sarc), llon + ((lon - llon) * harc / sarc));
736 case AREA:
737 GeomIterator bit = new GeomIterator(feature.geom);
738 bit.nextComp();
739 while (bit.hasNode()) {
740 Snode node = bit.next();
741 lat = node.lat;
742 lon = node.lon;
743 if (first) {
744 first = false;
745 } else {
746 double arc = (Math.acos(Math.cos(lon - llon) * Math.cos(lat - llat)));
747 slat += ((lat + llat) / 2 * arc);
748 slon += ((lon + llon) / 2 * arc);
749 sarc += arc;
750 }
751 llon = lon;
752 llat = lat;
753 }
754 return new Snode((sarc > 0.0 ? slat / sarc : 0.0), (sarc > 0.0 ? slon / sarc : 0.0));
755 default:
756 }
757 return null;
758 }
759
760}
Note: See TracBrowser for help on using the repository browser.