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

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

save

File size: 12.7 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 id;
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 id = 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.id = id;
307 feature.flag = Fflag.POINT;
308 edge = null;
309 }
310
311 public void addNode(long id, double lat, double lon, double depth) {
312 nodes.put(id, new Dnode(Math.toRadians(lat), Math.toRadians(lon), depth));
313 feature = new Feature();
314 feature.id = id;
315 feature.flag = Fflag.POINT;
316 edge = null;
317 }
318
319 public void addEdge(long id) {
320 feature = new Feature();
321 feature.id = id;
322 feature.flag = Fflag.LINE;
323 edge = new Edge();
324 }
325
326 public void addToEdge(long node) {
327 if (edge.first == 0) {
328 edge.first = node;
329 } else {
330 if (edge.last != 0) {
331 edge.nodes.add(edge.last);
332 }
333 edge.last = node;
334 }
335 }
336
337 public void addArea(long id) {
338 feature = new Feature();
339 feature.id = id;
340 feature.flag = Fflag.AREA;
341 outers = new ArrayList<Long>();
342 inners = new ArrayList<Long>();
343 edge = null;
344 }
345
346 public void addToArea(long id, boolean outer) {
347 if (outer) {
348 outers.add(id);
349 } else {
350 inners.add(id);
351 }
352 }
353
354 public void addTag(String key, String val) {
355 String subkeys[] = key.split(":");
356 if ((subkeys.length > 1) && subkeys[0].equals("seamark")) {
357 Obj obj = S57obj.enumType(subkeys[1]);
358 if ((subkeys.length > 2) && (obj != Obj.UNKOBJ)) {
359 int idx = 0;
360 Att att = Att.UNKATT;
361 try {
362 idx = Integer.parseInt(subkeys[2]);
363 if (subkeys.length == 4) {
364 att = s57.S57att.enumAttribute(subkeys[3], obj);
365 }
366 } catch (Exception e) {
367 att = S57att.enumAttribute(subkeys[2], obj);
368 }
369 ObjTab items = feature.objs.get(obj);
370 if (items == null) {
371 items = new ObjTab();
372 feature.objs.put(obj, items);
373 }
374 AttMap atts = items.get(idx);
375 if (atts == null) {
376 atts = new AttMap();
377 items.put(idx, atts);
378 }
379 AttVal<?> attval = S57val.convertValue(val, att);
380 if (attval.val != null)
381 atts.put(att, new AttItem(attval.conv, attval.val));
382 } else {
383 if (subkeys[1].equals("type")) {
384 feature.type = S57obj.enumType(val);
385 if (feature.objs.get(feature.type) == null) {
386 feature.objs.put(feature.type, new ObjTab());
387 }
388 } else {
389 Att att = S57att.enumAttribute(subkeys[1], Obj.UNKOBJ);
390 if (att != Att.UNKATT) {
391 AttVal<?> attval = S57val.convertValue(val, att);
392 if (attval.val != null)
393 feature.atts.put(att, new AttItem(attval.conv, attval.val));
394 }
395 }
396 }
397 }
398 }
399
400 public void tagsDone(long id) {
401 switch (feature.flag) {
402 case POINT:
403 Snode node = nodes.get(id);
404 if (node.flg != Nflag.CONN) {
405 node.flg = Nflag.ISOL;
406 }
407 feature.length = 0;
408 feature.area = 0;
409 break;
410 case LINE:
411 edges.put(id, edge);
412 nodes.get(edge.first).flg = Nflag.CONN;
413 nodes.get(edge.last).flg = Nflag.CONN;
414 Bound ebound = (new Bound(new Side(edge, true), true));
415 feature.length = calcLength(ebound);
416 if (edge.first == edge.last) {
417 feature.flag = Fflag.AREA;
418 Area area = new Area();
419 area.add(ebound);
420 feature.area = calcArea(ebound);
421 areas.put(id, area);
422 } else {
423 feature.area = 0;
424 }
425 break;
426 case AREA:
427 Bound bound = null;
428 Area area = new Area();
429 ArrayList<Long> role = outers;
430 while (role != null) {
431 while (!role.isEmpty()) {
432 Edge edge = edges.get(role.remove(0));
433 long node1 = edge.first;
434 long node2 = edge.last;
435 bound = new Bound(new Side(edge, true), (role == outers));
436 if (node1 != node2) {
437 for (ListIterator<Long> it = role.listIterator(0); it.hasNext();) {
438 Edge nedge = edges.get(it.next());
439 if (nedge.first == node2) {
440 bound.sides.add(new Side(nedge, true));
441 it.remove();
442 if (nedge.last == node2)
443 break;
444 } else if (nedge.last == node2) {
445 bound.sides.add(new Side(nedge, false));
446 it.remove();
447 if (nedge.first == node2)
448 break;
449 }
450 }
451 }
452 area.add(bound);
453 }
454 if (role == outers) {
455 feature.length = calcLength(bound);
456 feature.area = calcArea(bound);
457 role = inners;
458 } else {
459 role = null;
460 }
461 }
462 areas.put(id, area);
463 break;
464 case UNKN:
465 default:
466 break;
467 }
468 if ((feature.type != Obj.UNKOBJ) && !((edge != null) && (edge.last == 0))) {
469 index.put(id, feature);
470 if (features.get(feature.type) == null) {
471 features.put(feature.type, new ArrayList<Feature>());
472 }
473 feature.centre = findCentroid(feature);
474 features.get(feature.type).add(feature);
475 }
476 }
477
478 double signedArea(Bound bound) {
479 Snode node;
480 double lat, lon, llon, llat;
481 lat = lon = llon = llat = 0;
482 double sigma = 0;
483 BoundIterator it = new BoundIterator(bound);
484 while (it.hasNext()) {
485 llon = lon;
486 llat = lat;
487 node = it.next();
488 lat = node.lat;
489 lon = node.lon;
490 sigma += (lon * Math.sin(llat)) - (llon * Math.sin(lat));
491 }
492 return sigma / 2.0;
493 }
494
495 public boolean handOfArea(Bound bound) {
496 return (signedArea(bound) < 0);
497 }
498
499 public double calcArea(Bound bound) {
500 return Math.abs(signedArea(bound)) * 3444 * 3444;
501 }
502
503 public double calcLength(Bound bound) {
504 Snode node;
505 double lat, lon, llon, llat;
506 lat = lon = llon = llat = 0;
507 double sigma = 0;
508 BoundIterator it = new BoundIterator(bound);
509 if (it.hasNext()) {
510 node = it.next();
511 lat = node.lat;
512 lon = node.lon;
513 while (it.hasNext()) {
514 llon = lon;
515 llat = lat;
516 node = it.next();
517 lat = node.lat;
518 lon = node.lon;
519 sigma += Math.acos(Math.sin(lat) * Math.sin(llat) + Math.cos(lat) * Math.cos(llat) * Math.cos(llon - lon));
520 }
521 }
522 return sigma * 3444;
523 }
524
525 public Snode findCentroid(Feature feature) {
526 double lat, lon, slat, slon, llat, llon;
527 llat = llon = lat = lon = slat = slon = 0;
528 double sarc = 0;
529 boolean first = true;
530 switch (feature.flag) {
531 case POINT:
532 return nodes.get(feature.id);
533 case LINE:
534 Edge edge = edges.get(feature.id);
535 EdgeIterator eit = new EdgeIterator(edge, true);
536 while (eit.hasNext()) {
537 Snode node = eit.next();
538 lat = node.lat;
539 lon = node.lon;
540 if (first) {
541 first = false;
542 } else {
543 sarc += (Math.acos(Math.cos(lon - llon) * Math.cos(lat - llat)));
544 }
545 llat = lat;
546 llon = lon;
547 }
548 double harc = sarc / 2;
549 sarc = 0;
550 first = true;
551 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 if (sarc > harc)
561 break;
562 }
563 harc -= sarc;
564 llat = lat;
565 llon = lon;
566 }
567 return new Snode(llat + ((lat - llat) * harc / sarc), llon + ((lon - llon) * harc / sarc));
568 case AREA:
569 Bound bound = areas.get(feature.id).get(0);
570 BoundIterator bit = new BoundIterator(bound);
571 while (bit.hasNext()) {
572 Snode node = bit.next();
573 lat = node.lat;
574 lon = node.lon;
575 if (first) {
576 first = false;
577 } else {
578 double arc = (Math.acos(Math.cos(lon - llon) * Math.cos(lat - llat)));
579 slat += ((lat + llat) / 2 * arc);
580 slon += ((lon + llon) / 2 * arc);
581 sarc += arc;
582 }
583 llon = lon;
584 llat = lat;
585 }
586 return new Snode((sarc > 0.0 ? slat / sarc : 0.0), (sarc > 0.0 ? slon / sarc : 0.0));
587 default:
588 }
589 return null;
590 }
591
592}
Note: See TracBrowser for help on using the repository browser.