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

Last change on this file since 30187 was 30186, checked in by malcolmh, 11 years ago

save

File size: 13.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.S57att;
15import s57.S57att.*;
16import s57.S57obj;
17import s57.S57obj.*;
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 class Side { // An edge as used in a line or area feature
82 Edge edge; // Side is formed by this Edge...
83 boolean forward; // ... in this direction
84
85 public Side(Edge iedge, boolean ifwd) {
86 edge = iedge;
87 forward = ifwd;
88 }
89 }
90
91 public class Bound { // A single closed area
92 public boolean outer; // Role
93 ArrayList<Side> sides; // Sides that make up this area
94
95 public Bound() {
96 outer = true;
97 sides = new ArrayList<Side>();
98 }
99 public Bound(Side iside, boolean irole) {
100 outer = irole;
101 sides = new ArrayList<Side>();
102 sides.add(iside);
103 }
104 }
105
106 public class Area extends ArrayList<Bound> { // The collection of bounds for an area.
107 public Area() {
108 super();
109 }
110 }
111
112 public class AttItem {
113 public Conv conv;
114 public Object val;
115
116 AttItem(Conv iconv, Object ival) {
117 conv = iconv;
118 val = ival;
119 }
120 }
121
122 public class AttMap extends EnumMap<Att, AttItem> {
123 public AttMap() {
124 super(Att.class);
125 }
126 }
127
128 public class ObjTab extends HashMap<Integer, AttMap> {
129 public ObjTab() {
130 super();
131 }
132 }
133
134 public class ObjMap extends EnumMap<Obj, ObjTab> {
135 public ObjMap() {
136 super(Obj.class);
137 }
138 }
139
140 public class NodeTab extends HashMap<Long, Snode> {
141 public NodeTab() {
142 super();
143 }
144 }
145
146 public class EdgeTab extends HashMap<Long, Edge> {
147 public EdgeTab() {
148 super();
149 }
150 }
151
152 public class AreaTab extends HashMap<Long, Area> {
153 public AreaTab() {
154 super();
155 }
156 }
157
158 public class FtrMap extends EnumMap<Obj, ArrayList<Feature>> {
159 public FtrMap() {
160 super(Obj.class);
161 }
162 }
163
164 public class FtrTab extends HashMap<Long, Feature> {
165 public FtrTab() {
166 super();
167 }
168 }
169
170 public enum Fflag {
171 UNKN, POINT, LINE, AREA
172 }
173
174 public class Feature {
175 public Fflag flag;
176 public long refs;
177 public Obj type;
178 public AttMap atts;
179 public ObjMap objs;
180 public double area;
181 public double length;
182 public Snode centre;
183
184 Feature() {
185 flag = Fflag.UNKN;
186 refs = 0;
187 type = Obj.UNKOBJ;
188 atts = new AttMap();
189 objs = new ObjMap();
190 area = 0;
191 length = 0;
192 centre = new Snode();
193 }
194 }
195
196 public NodeTab nodes;
197 public EdgeTab edges;
198 public AreaTab areas;
199
200 public FtrMap features;
201 public FtrTab index;
202
203 private Feature feature;
204 private Edge edge;
205 private ArrayList<Long> outers;
206 private ArrayList<Long> inners;
207
208 public class EdgeIterator {
209 Edge edge;
210 boolean forward;
211 ListIterator<Long> it;
212
213 public EdgeIterator(Edge iedge, boolean dir) {
214 edge = iedge;
215 forward = dir;
216 it = null;
217 }
218
219 public boolean hasNext() {
220 return (edge != null);
221 }
222
223 public Snode next() {
224 long ref = 0;
225 if (forward) {
226 if (it == null) {
227 ref = edge.first;
228 it = edge.nodes.listIterator();
229 } else {
230 if (it.hasNext()) {
231 ref = it.next();
232 } else {
233 ref = edge.last;
234 edge = null;
235 }
236 }
237 } else {
238 if (it == null) {
239 ref = edge.last;
240 it = edge.nodes.listIterator(edge.nodes.size());
241 } else {
242 if (it.hasPrevious()) {
243 ref = it.previous();
244 } else {
245 ref = edge.first;
246 edge = null;
247 }
248 }
249 }
250 return nodes.get(ref);
251 }
252 }
253
254 public class BoundIterator {
255 Bound bound;
256 Side side;
257 ListIterator<Side> sit;
258 EdgeIterator eit;
259
260 public BoundIterator(Bound ibound) {
261 bound = ibound;
262 sit = bound.sides.listIterator();
263 if (sit.hasNext()) {
264 side = sit.next();
265 eit = new EdgeIterator(side.edge, side.forward);
266 } else {
267 side = null;
268 }
269 }
270
271 public boolean hasNext() {
272 return (side != null) && ((sit.hasNext()) || (eit.hasNext()));
273 }
274
275 public Snode next() {
276 Snode node = null;
277 if (side != null) {
278 if (eit.hasNext()) {
279 node = eit.next();
280 } else {
281 if (sit.hasNext()) {
282 side = sit.next();
283 eit = new EdgeIterator(side.edge, side.forward);
284 node = eit.next();
285 } else {
286 side = null;
287 }
288 }
289 }
290 return node;
291 }
292 }
293
294 public S57map() {
295 nodes = new NodeTab();
296 edges = new EdgeTab();
297 areas = new AreaTab();
298 feature = new Feature();
299 features = new FtrMap();
300 index = new FtrTab();
301 }
302
303 public void addNode(long id, double lat, double lon) {
304 nodes.put(id, new Snode(Math.toRadians(lat), Math.toRadians(lon)));
305 feature = new Feature();
306 feature.refs = id;
307 feature.flag = Fflag.POINT;
308 edge = null;
309 }
310
311 public void newNode(long id, double lat, double lon, Nflag flag) {
312 nodes.put(id, new Snode(Math.toRadians(lat), Math.toRadians(lon), flag));
313 if (flag == Nflag.ANON) {
314 edge.nodes.add(id);
315 }
316 }
317
318 public void newNode(long id, double lat, double lon, double depth) {
319 nodes.put(id, new Dnode(Math.toRadians(lat), Math.toRadians(lon), depth));
320 }
321
322 public void newEdge(long id) {
323 edge = new Edge();
324 edges.put(id, edge);
325 }
326
327 public void addConn(long id, int topi) {
328 if (topi == 1) {
329 edge.first = id;
330 } else {
331 edge.last = id;
332 }
333 }
334
335 public void addEdge(long id) {
336 feature = new Feature();
337 feature.refs = id;
338 feature.flag = Fflag.LINE;
339 edge = new Edge();
340 }
341
342 public void addToEdge(long node) {
343 if (edge.first == 0) {
344 edge.first = node;
345 } else {
346 if (edge.last != 0) {
347 edge.nodes.add(edge.last);
348 }
349 edge.last = node;
350 }
351 }
352
353 public void addArea(long id) {
354 feature = new Feature();
355 feature.refs = id;
356 feature.flag = Fflag.AREA;
357 outers = new ArrayList<Long>();
358 inners = new ArrayList<Long>();
359 edge = null;
360 }
361
362 public void addToArea(long id, boolean outer) {
363 if (outer) {
364 outers.add(id);
365 } else {
366 inners.add(id);
367 }
368 }
369
370 public void addTag(String key, String val) {
371 String subkeys[] = key.split(":");
372 if ((subkeys.length > 1) && subkeys[0].equals("seamark")) {
373 Obj obj = S57obj.enumType(subkeys[1]);
374 if ((subkeys.length > 2) && (obj != Obj.UNKOBJ)) {
375 int idx = 0;
376 Att att = Att.UNKATT;
377 try {
378 idx = Integer.parseInt(subkeys[2]);
379 if (subkeys.length == 4) {
380 att = s57.S57att.enumAttribute(subkeys[3], obj);
381 }
382 } catch (Exception e) {
383 att = S57att.enumAttribute(subkeys[2], obj);
384 }
385 ObjTab items = feature.objs.get(obj);
386 if (items == null) {
387 items = new ObjTab();
388 feature.objs.put(obj, items);
389 }
390 AttMap atts = items.get(idx);
391 if (atts == null) {
392 atts = new AttMap();
393 items.put(idx, atts);
394 }
395 AttVal<?> attval = S57val.convertValue(val, att);
396 if (attval.val != null)
397 atts.put(att, new AttItem(attval.conv, attval.val));
398 } else {
399 if (subkeys[1].equals("type")) {
400 feature.type = S57obj.enumType(val);
401 if (feature.objs.get(feature.type) == null) {
402 feature.objs.put(feature.type, new ObjTab());
403 }
404 } else {
405 Att att = S57att.enumAttribute(subkeys[1], Obj.UNKOBJ);
406 if (att != Att.UNKATT) {
407 AttVal<?> attval = S57val.convertValue(val, att);
408 if (attval.val != null)
409 feature.atts.put(att, new AttItem(attval.conv, attval.val));
410 }
411 }
412 }
413 }
414 }
415
416 public void tagsDone(long id) {
417 switch (feature.flag) {
418 case POINT:
419 Snode node = nodes.get(id);
420 if (node.flg != Nflag.CONN) {
421 node.flg = Nflag.ISOL;
422 }
423 feature.length = 0;
424 feature.area = 0;
425 break;
426 case LINE:
427 edges.put(id, edge);
428 nodes.get(edge.first).flg = Nflag.CONN;
429 nodes.get(edge.last).flg = Nflag.CONN;
430 Bound ebound = (new Bound(new Side(edge, true), true));
431 feature.length = calcLength(ebound);
432 if (edge.first == edge.last) {
433 feature.flag = Fflag.AREA;
434 Area area = new Area();
435 area.add(ebound);
436 feature.area = calcArea(ebound);
437 areas.put(id, area);
438 } else {
439 feature.area = 0;
440 }
441 break;
442 case AREA:
443 Bound bound = null;
444 Area area = new Area();
445 ArrayList<Long> role = outers;
446 while (role != null) {
447 while (!role.isEmpty()) {
448 Edge edge = edges.get(role.remove(0));
449 long node1 = edge.first;
450 long node2 = edge.last;
451 bound = new Bound(new Side(edge, true), (role == outers));
452 if (node1 != node2) {
453 for (ListIterator<Long> it = role.listIterator(0); it.hasNext();) {
454 Edge nedge = edges.get(it.next());
455 if (nedge.first == node2) {
456 bound.sides.add(new Side(nedge, true));
457 it.remove();
458 if (nedge.last == node2)
459 break;
460 } else if (nedge.last == node2) {
461 bound.sides.add(new Side(nedge, false));
462 it.remove();
463 if (nedge.first == node2)
464 break;
465 }
466 }
467 }
468 area.add(bound);
469 }
470 if (role == outers) {
471 feature.length = calcLength(bound);
472 feature.area = calcArea(bound);
473 role = inners;
474 } else {
475 role = null;
476 }
477 }
478 areas.put(id, area);
479 break;
480 case UNKN:
481 default:
482 break;
483 }
484 if ((feature.type != Obj.UNKOBJ) && !((edge != null) && (edge.last == 0))) {
485 index.put(id, feature);
486 if (features.get(feature.type) == null) {
487 features.put(feature.type, new ArrayList<Feature>());
488 }
489 feature.centre = findCentroid(feature);
490 features.get(feature.type).add(feature);
491 }
492 }
493
494 double signedArea(Bound bound) {
495 Snode node;
496 double lat, lon, llon, llat;
497 lat = lon = llon = llat = 0;
498 double sigma = 0;
499 BoundIterator it = new BoundIterator(bound);
500 while (it.hasNext()) {
501 llon = lon;
502 llat = lat;
503 node = it.next();
504 lat = node.lat;
505 lon = node.lon;
506 sigma += (lon * Math.sin(llat)) - (llon * Math.sin(lat));
507 }
508 return sigma / 2.0;
509 }
510
511 public boolean handOfArea(Bound bound) {
512 return (signedArea(bound) < 0);
513 }
514
515 public double calcArea(Bound bound) {
516 return Math.abs(signedArea(bound)) * 3444 * 3444;
517 }
518
519 public double calcLength(Bound bound) {
520 Snode node;
521 double lat, lon, llon, llat;
522 lat = lon = llon = llat = 0;
523 double sigma = 0;
524 BoundIterator it = new BoundIterator(bound);
525 if (it.hasNext()) {
526 node = it.next();
527 lat = node.lat;
528 lon = node.lon;
529 while (it.hasNext()) {
530 llon = lon;
531 llat = lat;
532 node = it.next();
533 lat = node.lat;
534 lon = node.lon;
535 sigma += Math.acos(Math.sin(lat) * Math.sin(llat) + Math.cos(lat) * Math.cos(llat) * Math.cos(llon - lon));
536 }
537 }
538 return sigma * 3444;
539 }
540
541 public Snode findCentroid(Feature feature) {
542 double lat, lon, slat, slon, llat, llon;
543 llat = llon = lat = lon = slat = slon = 0;
544 double sarc = 0;
545 boolean first = true;
546 switch (feature.flag) {
547 case POINT:
548 return nodes.get(feature.refs);
549 case LINE:
550 Edge edge = edges.get(feature.refs);
551 EdgeIterator eit = new EdgeIterator(edge, true);
552 while (eit.hasNext()) {
553 Snode node = eit.next();
554 lat = node.lat;
555 lon = node.lon;
556 if (first) {
557 first = false;
558 } else {
559 sarc += (Math.acos(Math.cos(lon - llon) * Math.cos(lat - llat)));
560 }
561 llat = lat;
562 llon = lon;
563 }
564 double harc = sarc / 2;
565 sarc = 0;
566 first = true;
567 eit = new EdgeIterator(edge, true);
568 while (eit.hasNext()) {
569 Snode node = eit.next();
570 lat = node.lat;
571 lon = node.lon;
572 if (first) {
573 first = false;
574 } else {
575 sarc = (Math.acos(Math.cos(lon - llon) * Math.cos(lat - llat)));
576 if (sarc > harc)
577 break;
578 }
579 harc -= sarc;
580 llat = lat;
581 llon = lon;
582 }
583 return new Snode(llat + ((lat - llat) * harc / sarc), llon + ((lon - llon) * harc / sarc));
584 case AREA:
585 Bound bound = areas.get(feature.refs).get(0);
586 BoundIterator bit = new BoundIterator(bound);
587 while (bit.hasNext()) {
588 Snode node = bit.next();
589 lat = node.lat;
590 lon = node.lon;
591 if (first) {
592 first = false;
593 } else {
594 double arc = (Math.acos(Math.cos(lon - llon) * Math.cos(lat - llat)));
595 slat += ((lat + llat) / 2 * arc);
596 slon += ((lon + llon) / 2 * arc);
597 sarc += arc;
598 }
599 llon = lon;
600 llat = lat;
601 }
602 return new Snode((sarc > 0.0 ? slat / sarc : 0.0), (sarc > 0.0 ? slon / sarc : 0.0));
603 default:
604 }
605 return null;
606 }
607
608}
Note: See TracBrowser for help on using the repository browser.