Mercurial > dive4elements > river
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 : |