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

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

save

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