comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/CrossSectionKMService.java @ 3318:dbe2f85bf160

merged flys-artifacts/2.8
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:35 +0200
parents f021080cb409
children
comparison
equal deleted inserted replaced
2987:98c7a46ec5ae 3318:dbe2f85bf160
1 package de.intevation.flys.artifacts.services;
2
3 import de.intevation.artifacts.CallMeta;
4 import de.intevation.artifacts.GlobalContext;
5
6 import de.intevation.artifacts.common.utils.XMLUtils;
7
8 import de.intevation.flys.artifacts.cache.CacheFactory;
9
10 import de.intevation.flys.backend.SessionHolder;
11
12 import de.intevation.flys.model.CrossSection;
13 import de.intevation.flys.model.CrossSectionLine;
14
15 import java.util.AbstractMap;
16 import java.util.ArrayDeque;
17 import java.util.Deque;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.NavigableMap;
21
22 import java.util.concurrent.ConcurrentSkipListMap;
23
24 import net.sf.ehcache.Cache;
25
26 import org.apache.log4j.Logger;
27
28 import org.hibernate.Query;
29 import org.hibernate.Session;
30
31 import org.w3c.dom.Document;
32 import org.w3c.dom.Element;
33 import org.w3c.dom.NodeList;
34
35
36 /**
37 * Service to find the next/previous km (measurement) of cross sections.
38 * Looking at the query for a single cross-section id at a single km, the
39 * service does the following:
40 *
41 * It returns the km itself if a measurement at that km was found and
42 * the N nearest other measurement points in both directions.
43 *
44 * That means, you can pass N=0 to find out whether a measurement at given km
45 * exists.
46 *
47 * If less than N neighbours exist in one direction, less are delivered
48 * (e.g. given measurements at [0,2,3,4,5,7,8,9] a query for km=8, N=3 will
49 * result in [4,5,7,8,9]).
50 */
51 public class CrossSectionKMService
52 extends FLYSService
53 {
54 private static Logger logger =
55 Logger.getLogger(CrossSectionKMService.class);
56
57 public static final String CACHE_NAME = "cross-section-kms";
58
59
60 /** Trivial constructor. */
61 public CrossSectionKMService() {
62 }
63
64
65 /**
66 * @param data
67 */
68 @Override
69 public Document doProcess(
70 Document data,
71 GlobalContext globalContext,
72 CallMeta callMeta
73 ) {
74 logger.debug("CrossSectionKMService.doProcess");
75
76 NodeList crossSectionNodes =
77 data.getElementsByTagName("art:cross-section");
78
79 Document document = XMLUtils.newDocument();
80
81 Element all = document.createElement("cross-sections");
82
83 for (int i = 0, CS = crossSectionNodes.getLength(); i < CS; ++i) {
84 Element crossSectionElement = (Element)crossSectionNodes.item(i);
85
86 String idString = crossSectionElement.getAttribute("id");
87 String kmString = crossSectionElement.getAttribute("km");
88 String neighborsString = crossSectionElement.getAttribute("n");
89
90 if (idString.length() == 0 || kmString.length() == 0) {
91 logger.debug("missing attributes in cross-section element");
92 continue;
93 }
94
95 double km;
96 Integer crossSectionId;
97 int N = 2;
98
99 try {
100 km = Double.parseDouble(kmString);
101 crossSectionId = Integer.valueOf(idString);
102
103 if (neighborsString.length() > 0) {
104 N = Integer.parseInt(neighborsString);
105 }
106 }
107 catch (NumberFormatException nfe) {
108 logger.debug("converting number failed", nfe);
109 continue;
110 }
111
112 NavigableMap<Double, Integer> map = getKms(crossSectionId);
113
114 if (map == null) {
115 logger.debug("cannot find cross section " + crossSectionId);
116 continue;
117 }
118
119 Deque<Map.Entry<Double, Integer>> result =
120 nearestNeighbors(map, km, N);
121
122 if (!result.isEmpty()) {
123 Element csE = document.createElement("cross-section");
124 csE.setAttribute("id", idString);
125 for (Map.Entry<Double, Integer> entry: result) {
126 Element lineE = document.createElement("line");
127 lineE.setAttribute(
128 "line-id", String.valueOf(entry.getValue()));
129 lineE.setAttribute(
130 "km", String.valueOf(entry.getKey()));
131 csE.appendChild(lineE);
132 }
133 all.appendChild(csE);
134 }
135 }
136
137 document.appendChild(all);
138
139 return document;
140 }
141
142 public static NavigableMap<Double, Integer> getKms(int crossSectionId) {
143
144 Cache cache = CacheFactory.getCache(CACHE_NAME);
145
146 if (cache == null) {
147 return getUncached(crossSectionId);
148 }
149
150 NavigableMap<Double, Integer> map;
151
152 net.sf.ehcache.Element element = cache.get(crossSectionId);
153 if (element == null) {
154 map = getUncached(crossSectionId);
155 if (map != null) {
156 element = new net.sf.ehcache.Element(
157 crossSectionId, map);
158 cache.put(element);
159 }
160 }
161 else {
162 map = (NavigableMap<Double, Integer>)element.getValue();
163 }
164
165 return map;
166 }
167
168
169 /**
170 * @param km the kilometer from which to start searching for other
171 * measurements
172 * @param N number of neighboring measurements to find.
173 */
174 public static Deque<Map.Entry<Double, Integer>> nearestNeighbors(
175 NavigableMap<Double, Integer> map,
176 double km,
177 int N
178 ) {
179 Deque<Map.Entry<Double, Integer>> result =
180 new ArrayDeque<Map.Entry<Double, Integer>>(2*N);
181
182 Integer v = map.get(km);
183
184 if (v != null) {
185 result.add(new AbstractMap.SimpleEntry<Double, Integer>(km, v));
186 }
187
188 int i = 0;
189 for (Map.Entry<Double, Integer> entry:
190 map.headMap(km, false).descendingMap().entrySet()) {
191 if (i++ >= N) {
192 break;
193 }
194 result.addFirst(entry);
195 }
196
197 i = 0;
198 for (Map.Entry<Double, Integer> entry:
199 map.tailMap(km, false).entrySet()) {
200 if (i++ >= N) {
201 break;
202 }
203 result.addLast(entry);
204 }
205
206 return result;
207 }
208
209
210 /**
211 * @param crossSectionId id of queried cross-section (in db).
212 * @return Mapping from kilometer to db-id.
213 */
214 public static NavigableMap<Double, Integer> getUncached(
215 Integer crossSectionId
216 ) {
217 NavigableMap<Double, Integer> result =
218 new ConcurrentSkipListMap<Double, Integer>();
219
220 Session session = SessionHolder.HOLDER.get();
221 Query query = session.createQuery(
222 "from CrossSection where id=:id");
223 query.setParameter("id", crossSectionId);
224
225 List<CrossSection> crossSections = query.list();
226 if (crossSections.isEmpty()) {
227 return null;
228 }
229
230 CrossSection crossSection = crossSections.get(0);
231 List<CrossSectionLine> lines = crossSection.getLines();
232
233 for (CrossSectionLine line: lines) {
234 Double km = line.getKm().doubleValue();
235 Integer id = line.getId();
236 result.put(km, id);
237 }
238
239 return result;
240 }
241 }
242 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org