svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.plugin / org.gvsig.labeling.app / org.gvsig.labeling.app.mainplugin / src / main / java / org / gvsig / labeling / label / GeneralLabelingStrategy.java @ 43510
History | View | Annotate | Download (29.3 KB)
1 | 40911 | jldominguez | package org.gvsig.labeling.label; |
---|---|---|---|
2 | |||
3 | import java.awt.Graphics2D; |
||
4 | import java.awt.geom.Point2D; |
||
5 | import java.awt.image.BufferedImage; |
||
6 | import java.util.ArrayList; |
||
7 | import java.util.Iterator; |
||
8 | 41227 | jldominguez | import java.util.List; |
9 | 40911 | jldominguez | import java.util.TreeMap; |
10 | import java.util.TreeSet; |
||
11 | 43510 | jjdelcerro | import org.cresques.cts.ICoordTrans; |
12 | 40911 | jldominguez | |
13 | 42980 | fdiaz | import org.slf4j.Logger; |
14 | import org.slf4j.LoggerFactory; |
||
15 | |||
16 | 40911 | jldominguez | import org.gvsig.compat.print.PrintAttributes; |
17 | import org.gvsig.fmap.dal.exception.DataException; |
||
18 | import org.gvsig.fmap.dal.exception.ReadException; |
||
19 | import org.gvsig.fmap.dal.feature.Feature; |
||
20 | import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor; |
||
21 | import org.gvsig.fmap.dal.feature.FeatureSet; |
||
22 | import org.gvsig.fmap.geom.Geometry; |
||
23 | import org.gvsig.fmap.geom.Geometry.SUBTYPES; |
||
24 | import org.gvsig.fmap.geom.Geometry.TYPES; |
||
25 | import org.gvsig.fmap.geom.GeometryException; |
||
26 | import org.gvsig.fmap.geom.GeometryLocator; |
||
27 | import org.gvsig.fmap.geom.GeometryManager; |
||
28 | 41227 | jldominguez | import org.gvsig.fmap.geom.aggregate.MultiPrimitive; |
29 | 42555 | fdiaz | import org.gvsig.fmap.geom.operation.GeometryOperationException; |
30 | import org.gvsig.fmap.geom.operation.GeometryOperationNotSupportedException; |
||
31 | 40911 | jldominguez | import org.gvsig.fmap.geom.primitive.Envelope; |
32 | import org.gvsig.fmap.geom.primitive.Point; |
||
33 | import org.gvsig.fmap.geom.type.GeometryType; |
||
34 | import org.gvsig.fmap.mapcontext.ViewPort; |
||
35 | import org.gvsig.fmap.mapcontext.layers.FLayer; |
||
36 | import org.gvsig.fmap.mapcontext.layers.vectorial.FLyrVect; |
||
37 | import org.gvsig.fmap.mapcontext.rendering.legend.styling.ILabelClass; |
||
38 | import org.gvsig.fmap.mapcontext.rendering.legend.styling.ILabelingMethod; |
||
39 | import org.gvsig.fmap.mapcontext.rendering.legend.styling.ILabelingStrategy; |
||
40 | import org.gvsig.fmap.mapcontext.rendering.legend.styling.IPlacementConstraints; |
||
41 | import org.gvsig.fmap.mapcontext.rendering.legend.styling.IZoomConstraints; |
||
42 | import org.gvsig.fmap.mapcontext.rendering.symbols.CartographicSupport; |
||
43 | import org.gvsig.i18n.Messages; |
||
44 | import org.gvsig.labeling.lang.LabelClassUtils; |
||
45 | import org.gvsig.labeling.placements.ILabelPlacement; |
||
46 | import org.gvsig.labeling.placements.LinePlacementConstraints; |
||
47 | import org.gvsig.labeling.placements.MultiShapePlacementConstraints; |
||
48 | import org.gvsig.labeling.placements.PlacementManager; |
||
49 | import org.gvsig.labeling.placements.PointPlacementConstraints; |
||
50 | import org.gvsig.labeling.placements.PolygonPlacementConstraints; |
||
51 | import org.gvsig.labeling.placements.RemoveDuplicatesComparator; |
||
52 | import org.gvsig.labeling.symbol.SmartTextSymbol; |
||
53 | import org.gvsig.labeling.symbol.SymbolUtils; |
||
54 | import org.gvsig.symbology.SymbologyLocator; |
||
55 | import org.gvsig.symbology.fmap.mapcontext.rendering.legend.styling.LabelLocationMetrics; |
||
56 | import org.gvsig.tools.ToolsLocator; |
||
57 | import org.gvsig.tools.dispose.DisposableIterator; |
||
58 | import org.gvsig.tools.dynobject.DynStruct; |
||
59 | import org.gvsig.tools.persistence.PersistenceManager; |
||
60 | import org.gvsig.tools.persistence.PersistentState; |
||
61 | import org.gvsig.tools.persistence.exception.PersistenceException; |
||
62 | import org.gvsig.tools.task.Cancellable; |
||
63 | 42555 | fdiaz | |
64 | 41789 | fdiaz | public class GeneralLabelingStrategy implements ILabelingStrategy, Cloneable, |
65 | CartographicSupport { |
||
66 | |||
67 | private static final Logger logger = LoggerFactory |
||
68 | .getLogger(GeneralLabelingStrategy.class); |
||
69 | |||
70 | public static final String GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME = "GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME"; |
||
71 | |||
72 | public static PointPlacementConstraints DefaultPointPlacementConstraints = new PointPlacementConstraints(); |
||
73 | public static LinePlacementConstraints DefaultLinePlacementConstraints = new LinePlacementConstraints(); |
||
74 | public static PolygonPlacementConstraints DefaultPolygonPlacementConstraints = new PolygonPlacementConstraints(); |
||
75 | |||
76 | 40911 | jldominguez | private static String[] NO_TEXT = { Messages.getText("text_field") }; |
77 | 41789 | fdiaz | |
78 | private static MultiShapePlacementConstraints DefaultMultiShapePlacementConstratints = new MultiShapePlacementConstraints(); |
||
79 | |||
80 | 40911 | jldominguez | private ILabelingMethod method;
|
81 | private IPlacementConstraints placementConstraints;
|
||
82 | private IZoomConstraints zoomConstraints;
|
||
83 | 41789 | fdiaz | |
84 | 40911 | jldominguez | private boolean allowOverlapping; |
85 | 41789 | fdiaz | |
86 | 40911 | jldominguez | protected FLyrVect layer;
|
87 | |||
88 | 41789 | fdiaz | // private long parseTime;
|
89 | 40911 | jldominguez | private int unit; |
90 | private int referenceSystem; |
||
91 | // private double sizeAfter;
|
||
92 | 41789 | fdiaz | private boolean printMode = false; /* |
93 | * indicate whether output is for a
|
||
94 | * print product (PDF, PS, ...)
|
||
95 | */
|
||
96 | |||
97 | 42980 | fdiaz | private List<Geometry> drawnGeometryLabels; |
98 | |||
99 | 40911 | jldominguez | public GeneralLabelingStrategy() {
|
100 | 41789 | fdiaz | method = SymbologyLocator.getSymbologyManager() |
101 | .createDefaultLabelingMethod(); |
||
102 | 40911 | jldominguez | } |
103 | |||
104 | public void setLayer(FLayer layer) { |
||
105 | FLyrVect l = (FLyrVect) layer; |
||
106 | this.layer = l;
|
||
107 | } |
||
108 | |||
109 | public ILabelingMethod getLabelingMethod() {
|
||
110 | return method;
|
||
111 | } |
||
112 | |||
113 | public void setLabelingMethod(ILabelingMethod method) { |
||
114 | this.method = method;
|
||
115 | } |
||
116 | |||
117 | 41789 | fdiaz | private class GeometryItem { |
118 | 40911 | jldominguez | public Geometry geom = null; |
119 | public int weigh = 0; |
||
120 | public double savedPerimeter; |
||
121 | |||
122 | 41789 | fdiaz | public GeometryItem(Geometry geom, int weigh) { |
123 | 40911 | jldominguez | this.geom = geom;
|
124 | this.weigh = weigh;
|
||
125 | this.savedPerimeter = 0; |
||
126 | } |
||
127 | } |
||
128 | 41789 | fdiaz | |
129 | 42980 | fdiaz | public void draw(BufferedImage mapImage, Graphics2D mapGraphics, double scale, ViewPort viewPort, |
130 | Cancellable cancel, double dpi) throws ReadException { |
||
131 | 40911 | jldominguez | |
132 | 42980 | fdiaz | drawnGeometryLabels = new ArrayList<Geometry>(1000); |
133 | 41789 | fdiaz | |
134 | 42980 | fdiaz | int x = (int) viewPort.getOffset().getX(); |
135 | int y = (int) viewPort.getOffset().getY(); |
||
136 | 40911 | jldominguez | |
137 | 42980 | fdiaz | // offsets for page generation (PDF, PS, direct printing)
|
138 | int print_offset_x = x;
|
||
139 | int print_offset_y = y;
|
||
140 | if (printMode) {
|
||
141 | // for printing, we never offset the labels themselves
|
||
142 | x = 0;
|
||
143 | y = 0;
|
||
144 | printMode = false;
|
||
145 | } |
||
146 | 41789 | fdiaz | |
147 | 42980 | fdiaz | TreeMap<String[], GeometryItem> labelsToPlace = null; |
148 | 40911 | jldominguez | |
149 | 42980 | fdiaz | String[] usedFields = getUsedFields(); |
150 | 40911 | jldominguez | |
151 | 42980 | fdiaz | int notPlacedCount = 0; |
152 | int placedCount = 0; |
||
153 | 40911 | jldominguez | |
154 | 42980 | fdiaz | /*
|
155 | * Get the label placement solvers according the user's settings
|
||
156 | */
|
||
157 | ILabelPlacement placement = PlacementManager.getPlacement(getPlacementConstraints(), layer.getShapeType()); |
||
158 | 40911 | jldominguez | |
159 | 42980 | fdiaz | BufferedImage targetBI;
|
160 | Graphics2D targetGr;
|
||
161 | 40911 | jldominguez | |
162 | 42980 | fdiaz | /*
|
163 | * get an ordered set of the LabelClasses up on the label priority
|
||
164 | */
|
||
165 | ILabelClass[] lcs = method.getLabelClasses();
|
||
166 | TreeSet<ILabelClass> ts = new TreeSet<ILabelClass>(new LabelClassComparatorByPriority()); |
||
167 | 40911 | jldominguez | |
168 | 42980 | fdiaz | for (int i = 0; i < lcs.length; i++) |
169 | ts.add(lcs[i]); |
||
170 | 40911 | jldominguez | |
171 | 42980 | fdiaz | if (ts.size() == 0) |
172 | return;
|
||
173 | 41789 | fdiaz | |
174 | 42980 | fdiaz | /*
|
175 | * now we have an ordered set, it is only need to give a pass for each
|
||
176 | * label class to render by priorities.
|
||
177 | *
|
||
178 | * If no priorities were defined, the following loop only executes once
|
||
179 | */
|
||
180 | for (ILabelClass lc : ts) {
|
||
181 | 41789 | fdiaz | |
182 | 42980 | fdiaz | if (!lc.isVisible(scale)) {
|
183 | /*
|
||
184 | * Avoid non-visible labels
|
||
185 | */
|
||
186 | continue;
|
||
187 | } |
||
188 | 40911 | jldominguez | |
189 | 42980 | fdiaz | FeatureSet fset = null;
|
190 | DisposableIterator diter = null;
|
||
191 | try {
|
||
192 | 40911 | jldominguez | |
193 | 42980 | fdiaz | try {
|
194 | fset = method.getFeatureIteratorByLabelClass(layer, lc, viewPort, usedFields); |
||
195 | } catch (DataException e) {
|
||
196 | throw new ReadException(layer.getFeatureStore().getProviderName(), e); |
||
197 | } |
||
198 | 40911 | jldominguez | |
199 | 42980 | fdiaz | // duplicates treatment stuff
|
200 | /* handle the duplicates mode */
|
||
201 | int duplicateMode = getDuplicateLabelsMode();
|
||
202 | if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
|
||
203 | // we need to register the labels already placed
|
||
204 | 40911 | jldominguez | |
205 | 42980 | fdiaz | labelsToPlace = new TreeMap<String[], GeometryItem>(new RemoveDuplicatesComparator()); |
206 | } |
||
207 | 40911 | jldominguez | |
208 | 42980 | fdiaz | boolean bLabelsReallocatable = !isAllowingOverlap();
|
209 | 41790 | fdiaz | |
210 | 42980 | fdiaz | BufferedImage overlapDetectImage = null; |
211 | Graphics2D overlapDetectGraphics = null; |
||
212 | if (bLabelsReallocatable) {
|
||
213 | int width = viewPort.getImageWidth() + print_offset_x;
|
||
214 | 41790 | fdiaz | |
215 | 42980 | fdiaz | if (width < 0) { |
216 | width = 1;
|
||
217 | } |
||
218 | int height = viewPort.getImageHeight() + print_offset_y;
|
||
219 | if (height < 0) { |
||
220 | height = 1;
|
||
221 | } |
||
222 | if (mapImage != null) |
||
223 | overlapDetectImage = |
||
224 | new BufferedImage(mapImage.getWidth() + print_offset_x, mapImage.getHeight() |
||
225 | + print_offset_y, BufferedImage.TYPE_INT_ARGB);
|
||
226 | else
|
||
227 | overlapDetectImage = |
||
228 | new BufferedImage(viewPort.getImageWidth() + print_offset_x, viewPort.getImageHeight() |
||
229 | + print_offset_y, BufferedImage.TYPE_INT_ARGB);
|
||
230 | 40911 | jldominguez | |
231 | 42980 | fdiaz | overlapDetectGraphics = overlapDetectImage.createGraphics(); |
232 | overlapDetectGraphics.setRenderingHints(mapGraphics.getRenderingHints()); |
||
233 | } |
||
234 | if (bLabelsReallocatable) {
|
||
235 | targetBI = overlapDetectImage; |
||
236 | targetGr = overlapDetectGraphics; |
||
237 | targetGr.translate(-x, -y); |
||
238 | } else {
|
||
239 | targetBI = mapImage; |
||
240 | targetGr = mapGraphics; |
||
241 | } |
||
242 | 40911 | jldominguez | |
243 | 42980 | fdiaz | try {
|
244 | diter = fset.fastIterator(); |
||
245 | } catch (DataException e) {
|
||
246 | throw new ReadException(layer.getFeatureStore().getProviderName(), e); |
||
247 | } |
||
248 | Feature featu = null;
|
||
249 | Geometry geome = null;
|
||
250 | 40911 | jldominguez | |
251 | 42980 | fdiaz | while (!cancel.isCanceled() && diter.hasNext()) {
|
252 | 40911 | jldominguez | |
253 | 43313 | fdiaz | featu = ((Feature) diter.next()).getCopy(); |
254 | 42980 | fdiaz | geome = featu.getDefaultGeometry(); |
255 | if (geome == null || geome.getType() == Geometry.TYPES.NULL) { |
||
256 | continue;
|
||
257 | } |
||
258 | 43510 | jjdelcerro | ICoordTrans ct = layer.getCoordTrans(); |
259 | if( ct!=null ) { |
||
260 | geome.reProject(ct); |
||
261 | } |
||
262 | 42980 | fdiaz | if (!setupLabel(featu, lc, cancel, usedFields, viewPort, dpi, duplicateMode)) {
|
263 | continue;
|
||
264 | } |
||
265 | 41789 | fdiaz | |
266 | 42980 | fdiaz | String[] texts = lc.getTexts(); |
267 | 41789 | fdiaz | |
268 | 42980 | fdiaz | if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
|
269 | // check if this text (so label) is already present in
|
||
270 | // the map
|
||
271 | 41789 | fdiaz | |
272 | 42980 | fdiaz | GeometryItem item = labelsToPlace.get(texts); |
273 | if (item == null) { |
||
274 | item = new GeometryItem(geome, 0); |
||
275 | labelsToPlace.put(texts, item); |
||
276 | } |
||
277 | if (item.geom != null) { |
||
278 | 41789 | fdiaz | |
279 | 42980 | fdiaz | notPlacedCount++; |
280 | if (geome.getType() != Geometry.TYPES.POINT) {
|
||
281 | 41789 | fdiaz | |
282 | 42980 | fdiaz | Envelope auxBox = geome.getEnvelope(); |
283 | double perimeterAux = 2 * (auxBox.getLength(0) + auxBox.getLength(1)); |
||
284 | if (perimeterAux > item.savedPerimeter) {
|
||
285 | item.geom = geome; // FConverter.jts_to_igeometry(jtsGeom);
|
||
286 | item.savedPerimeter = perimeterAux; |
||
287 | } |
||
288 | } else {
|
||
289 | int weigh = item.weigh;
|
||
290 | 41789 | fdiaz | |
291 | 42980 | fdiaz | try {
|
292 | Point pointFromLabel = item.geom.centroid();
|
||
293 | Point pointGeome = geome.centroid();
|
||
294 | item.geom = |
||
295 | GeometryLocator.getGeometryManager().createPoint( |
||
296 | (pointFromLabel.getX() * weigh + pointGeome.getX()) / (weigh + 1),
|
||
297 | (pointFromLabel.getY() * weigh + pointGeome.getY()) / (weigh + 1),
|
||
298 | Geometry.SUBTYPES.GEOM2D); |
||
299 | } catch (Exception ex) { |
||
300 | throw new ReadException(layer.getFeatureStore().getProviderName(), ex); |
||
301 | } |
||
302 | 40911 | jldominguez | |
303 | 42980 | fdiaz | } |
304 | } else {
|
||
305 | item.geom = geome; |
||
306 | } |
||
307 | item.weigh++; |
||
308 | } else {
|
||
309 | // Check if size is a pixel
|
||
310 | if (isOnePoint(viewPort, geome)) {
|
||
311 | continue;
|
||
312 | } |
||
313 | 41789 | fdiaz | |
314 | 42980 | fdiaz | List<Geometry> geome_parts = new ArrayList<Geometry>(); |
315 | if (duplicateMode == IPlacementConstraints.ONE_LABEL_PER_FEATURE_PART) {
|
||
316 | geome_parts = getGeometryParts(geome); |
||
317 | } else {
|
||
318 | geome_parts.add(geome); |
||
319 | } |
||
320 | 41789 | fdiaz | |
321 | 42980 | fdiaz | try {
|
322 | int n = geome_parts.size();
|
||
323 | for (int k = 0; k < n; k++) { |
||
324 | drawLabelInGeom(targetBI, targetGr, lc, placement, viewPort, geome_parts.get(k), |
||
325 | cancel, dpi, bLabelsReallocatable); |
||
326 | } |
||
327 | } catch (GeometryException e) {
|
||
328 | throw new ReadException(layer.getFeatureStore().getProviderName(), e); |
||
329 | } |
||
330 | 41789 | fdiaz | |
331 | 42980 | fdiaz | placedCount++; |
332 | } |
||
333 | } |
||
334 | 41789 | fdiaz | |
335 | 42980 | fdiaz | // ======= End iteration in feature set ====================
|
336 | 40911 | jldominguez | |
337 | 42980 | fdiaz | if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
|
338 | Iterator<String[]> textsIt = labelsToPlace.keySet().iterator(); |
||
339 | while (!cancel.isCanceled() && textsIt.hasNext()) {
|
||
340 | notPlacedCount++; |
||
341 | String[] texts = textsIt.next(); |
||
342 | 40911 | jldominguez | |
343 | 42980 | fdiaz | GeometryItem item = labelsToPlace.get(texts); |
344 | if (item != null) { |
||
345 | lc.setTexts(texts); |
||
346 | // Check if size is a pixel
|
||
347 | if (isOnePoint(viewPort, item.geom)) {
|
||
348 | continue;
|
||
349 | } |
||
350 | 40911 | jldominguez | |
351 | 42980 | fdiaz | try {
|
352 | drawLabelInGeom(targetBI, targetGr, lc, placement, viewPort, item.geom, cancel, dpi, |
||
353 | bLabelsReallocatable); |
||
354 | } catch (GeometryException e) {
|
||
355 | throw new ReadException(layer.getFeatureStore().getProviderName(), e); |
||
356 | } |
||
357 | } |
||
358 | } |
||
359 | } |
||
360 | 41790 | fdiaz | |
361 | 42980 | fdiaz | if (bLabelsReallocatable) {
|
362 | targetGr.translate(x, y); |
||
363 | if (mapImage != null) { |
||
364 | mapGraphics.drawImage(overlapDetectImage, null, null); |
||
365 | } else {
|
||
366 | mapGraphics.drawImage(overlapDetectImage, null, null); |
||
367 | } |
||
368 | } |
||
369 | 40911 | jldominguez | |
370 | 42980 | fdiaz | } finally {
|
371 | if (diter != null) { |
||
372 | diter.dispose(); |
||
373 | } |
||
374 | if (fset != null) { |
||
375 | fset.dispose(); |
||
376 | } |
||
377 | } |
||
378 | } // big iteration
|
||
379 | 41789 | fdiaz | |
380 | 42980 | fdiaz | } |
381 | |||
382 | 41227 | jldominguez | private List<Geometry> getGeometryParts(Geometry ge) { |
383 | |||
384 | 41789 | fdiaz | List<Geometry> resp = new ArrayList<Geometry>(); |
385 | if (ge != null) { |
||
386 | if (ge instanceof MultiPrimitive) { |
||
387 | MultiPrimitive mp = (MultiPrimitive) ge; |
||
388 | int n = mp.getPrimitivesNumber();
|
||
389 | for (int i = 0; i < n; i++) { |
||
390 | resp.add(mp.getPrimitiveAt(i)); |
||
391 | } |
||
392 | } else {
|
||
393 | resp.add(ge); |
||
394 | } |
||
395 | } |
||
396 | return resp;
|
||
397 | } |
||
398 | 40911 | jldominguez | |
399 | 41789 | fdiaz | private void drawLabelInGeom(BufferedImage targetBI, Graphics2D targetGr, |
400 | ILabelClass lc, ILabelPlacement placement, ViewPort viewPort, |
||
401 | Geometry geom, Cancellable cancel, double dpi,
|
||
402 | boolean bLabelsReallocatable) throws GeometryException { |
||
403 | |||
404 | 40911 | jldominguez | lc.toCartographicSize(viewPort, dpi, null);
|
405 | ArrayList<LabelLocationMetrics> llm = null; |
||
406 | 41789 | fdiaz | llm = placement.guess(lc, geom, getPlacementConstraints(), 0, cancel,
|
407 | viewPort); |
||
408 | 40911 | jldominguez | |
409 | setReferenceSystem(lc.getReferenceSystem()); |
||
410 | setUnit(lc.getUnit()); |
||
411 | |||
412 | /*
|
||
413 | 41789 | fdiaz | * search if there is room left by the previous and with more priority
|
414 | * labels, then check the current level
|
||
415 | 40911 | jldominguez | */
|
416 | 41789 | fdiaz | lookupAndPlaceLabel(targetBI, targetGr, llm, placement, lc, geom, |
417 | viewPort, cancel, bLabelsReallocatable); |
||
418 | 40911 | jldominguez | |
419 | } |
||
420 | |||
421 | private int getDuplicateLabelsMode() { |
||
422 | if (getPlacementConstraints() == null) { |
||
423 | return IPlacementConstraints.DefaultDuplicateLabelsMode;
|
||
424 | } |
||
425 | return getPlacementConstraints().getDuplicateLabelsMode();
|
||
426 | } |
||
427 | |||
428 | private boolean lookupAndPlaceLabel(BufferedImage bi, Graphics2D g, |
||
429 | ArrayList<LabelLocationMetrics> llm, ILabelPlacement placement,
|
||
430 | 41789 | fdiaz | ILabelClass lc, Geometry geom, ViewPort viewPort, |
431 | 40911 | jldominguez | Cancellable cancel, boolean bLabelsReallocatable)
|
432 | 41789 | fdiaz | throws GeometryException {
|
433 | |||
434 | 42980 | fdiaz | GeometryManager gm = GeometryLocator.getGeometryManager(); |
435 | 40911 | jldominguez | int i;
|
436 | for (i = 0; !cancel.isCanceled() && i < llm.size(); i++) { |
||
437 | LabelLocationMetrics labelMetrics = llm.get(i); |
||
438 | |||
439 | IPlacementConstraints pc = getPlacementConstraints(); |
||
440 | 41789 | fdiaz | if (pc instanceof MultiShapePlacementConstraints) { |
441 | MultiShapePlacementConstraints mpc = (MultiShapePlacementConstraints) pc; |
||
442 | |||
443 | 40911 | jldominguez | GeometryType geom_gt = null;
|
444 | 41789 | fdiaz | |
445 | 40911 | jldominguez | geom_gt = gm.getGeometryType(geom.getType(), SUBTYPES.GEOM2D); |
446 | |||
447 | 41789 | fdiaz | if (geom_gt.getType() == TYPES.POINT
|
448 | || geom_gt.getType() == TYPES.MULTIPOINT) { |
||
449 | 40911 | jldominguez | pc = mpc.getPointConstraints(); |
450 | } else {
|
||
451 | 41789 | fdiaz | if (geom_gt.isTypeOf(TYPES.CURVE)
|
452 | || geom_gt.getType() == TYPES.MULTICURVE) { |
||
453 | 40911 | jldominguez | pc = mpc.getLineConstraints(); |
454 | } else {
|
||
455 | 41789 | fdiaz | if (geom_gt.isTypeOf(TYPES.SURFACE)
|
456 | || geom_gt.getType() == TYPES.MULTISURFACE) { |
||
457 | 40911 | jldominguez | pc = mpc.getPolygonConstraints(); |
458 | } |
||
459 | } |
||
460 | } |
||
461 | } |
||
462 | |||
463 | /*
|
||
464 | * Ver comentario en el metodo drawLabelInGeom
|
||
465 | */
|
||
466 | if (bLabelsReallocatable) {
|
||
467 | 41789 | fdiaz | |
468 | 40911 | jldominguez | Geometry aux_geom = null;
|
469 | aux_geom = lc.getShape(labelMetrics); |
||
470 | 41789 | fdiaz | |
471 | 40911 | jldominguez | if (!isOverlapping(bi, aux_geom)) {
|
472 | |||
473 | 41789 | fdiaz | if (!pc.isFollowingLine()) {
|
474 | 40911 | jldominguez | lc.draw(g, labelMetrics, geom); |
475 | } else {
|
||
476 | 41789 | fdiaz | |
477 | 41131 | jldominguez | ILabelClass smsLc = new SmartTextSymbolLabelClass();
|
478 | 41789 | fdiaz | SmartTextSymbol sms = new SmartTextSymbol(
|
479 | lc.getTextSymbol(), pc); |
||
480 | 40911 | jldominguez | |
481 | 41789 | fdiaz | double sizeBefore = lc.getTextSymbol().getFont()
|
482 | .getSize(); |
||
483 | double sizeAfter = SymbolUtils.getCartographicLength(
|
||
484 | this, sizeBefore, viewPort, viewPort.getDPI());
|
||
485 | 40911 | jldominguez | sms.setFontSize(sizeAfter); |
486 | |||
487 | smsLc.setTextSymbol(sms); |
||
488 | geom.transform(viewPort.getAffineTransform()); |
||
489 | smsLc.draw(g, null, geom);
|
||
490 | sms.setFontSize(sizeBefore); |
||
491 | |||
492 | } |
||
493 | return true; |
||
494 | } |
||
495 | } else {
|
||
496 | 41789 | fdiaz | if (!pc.isFollowingLine()) {
|
497 | 40911 | jldominguez | lc.draw(g, labelMetrics, null);
|
498 | 41789 | fdiaz | } else {
|
499 | ILabelClass smsLc = new SmartTextSymbolLabelClass();
|
||
500 | SmartTextSymbol sms = new SmartTextSymbol(
|
||
501 | lc.getTextSymbol(), pc); |
||
502 | 40911 | jldominguez | |
503 | double sizeBefore = lc.getTextSymbol().getFont().getSize();
|
||
504 | double sizeAfter = SymbolUtils.getCartographicLength(this, |
||
505 | 41789 | fdiaz | sizeBefore, viewPort, viewPort.getDPI()); |
506 | 40911 | jldominguez | sms.setFontSize(sizeAfter); |
507 | |||
508 | smsLc.setTextSymbol(sms); |
||
509 | geom.transform(viewPort.getAffineTransform()); |
||
510 | smsLc.draw(g, null, geom);
|
||
511 | |||
512 | sms.setFontSize(sizeBefore); |
||
513 | } |
||
514 | return true; |
||
515 | } |
||
516 | } |
||
517 | return false; |
||
518 | } |
||
519 | |||
520 | /**
|
||
521 | 41789 | fdiaz | * Divide una cadena de caracteres por el caracter dos puntos siempre que no
|
522 | * est? entre comillas.
|
||
523 | 40911 | jldominguez | *
|
524 | * @param str
|
||
525 | * Cadena de caracteres
|
||
526 | *
|
||
527 | * @return String[]
|
||
528 | *
|
||
529 | */
|
||
530 | 41789 | fdiaz | private String[] divideExpression(String str) { |
531 | 40911 | jldominguez | ArrayList<String> r = new ArrayList<String>(); |
532 | boolean inQuotationMarks = false; |
||
533 | int lastIndex = 0; |
||
534 | 41789 | fdiaz | for (int i = 0; i < str.length(); i++) { |
535 | 42171 | jbadia | String currentChar = str.substring(i, i + 1); |
536 | if (currentChar.compareTo("\"") == 0 ) { |
||
537 | 40911 | jldominguez | inQuotationMarks = !inQuotationMarks; |
538 | 42171 | jbadia | // Si es el cierre de las comillas
|
539 | if(!inQuotationMarks){
|
||
540 | r.add(str.substring(lastIndex, i + 1).replace("\"", "'")); |
||
541 | lastIndex = i + 1;
|
||
542 | } |
||
543 | 40911 | jldominguez | } |
544 | 42171 | jbadia | if (currentChar.compareTo(":") == 0 |
545 | 41789 | fdiaz | && !inQuotationMarks) { |
546 | if (lastIndex < i) {
|
||
547 | 40911 | jldominguez | r.add(str.substring(lastIndex, i)); |
548 | } |
||
549 | 41789 | fdiaz | lastIndex = i + 1;
|
550 | 40911 | jldominguez | } |
551 | } |
||
552 | 41789 | fdiaz | if (lastIndex < str.length() - 1) { |
553 | 40911 | jldominguez | r.add(str.substring(lastIndex)); |
554 | } |
||
555 | String[] result = new String[r.size()]; |
||
556 | r.toArray(result); |
||
557 | return result;
|
||
558 | } |
||
559 | |||
560 | /**
|
||
561 | * Compute the texts to show in the label and store them in LabelClass.
|
||
562 | */
|
||
563 | @SuppressWarnings("unchecked") |
||
564 | private boolean setupLabel(Feature featu, ILabelClass lc, |
||
565 | Cancellable cancel, String[] usedFields, ViewPort viewPort, |
||
566 | 41789 | fdiaz | double dpi, int duplicateMode) { |
567 | 40911 | jldominguez | |
568 | String expr = lc.getStringLabelExpression();
|
||
569 | |||
570 | long pt1 = System.currentTimeMillis(); |
||
571 | String[] texts = NO_TEXT; |
||
572 | 41421 | jjdelcerro | List<String> preTexts = new ArrayList<String>(); |
573 | 40911 | jldominguez | try {
|
574 | if (expr != null) { |
||
575 | |||
576 | if (expr.equals("")) { |
||
577 | expr = texts[0];
|
||
578 | } |
||
579 | |||
580 | String[] multiexpr = divideExpression(expr); |
||
581 | 41789 | fdiaz | for (int i = 0; i < multiexpr.length; i++) { |
582 | |||
583 | 40911 | jldominguez | expr = multiexpr[i]; |
584 | 41789 | fdiaz | Object res = LabelClassUtils.evaluate(expr,
|
585 | featu.getEvaluatorData()); |
||
586 | 40911 | jldominguez | if (res != null) { |
587 | preTexts.add(res.toString()); |
||
588 | } else {
|
||
589 | 41421 | jjdelcerro | preTexts.add("");
|
590 | 40911 | jldominguez | } |
591 | } |
||
592 | texts = new String[preTexts.size()]; |
||
593 | preTexts.toArray(texts); |
||
594 | 41789 | fdiaz | // parseTime += System.currentTimeMillis()-pt1;
|
595 | 40911 | jldominguez | } |
596 | lc.setTexts(texts); |
||
597 | |||
598 | } catch (Exception e) { |
||
599 | 41421 | jjdelcerro | logger.warn("While setting up label", e);
|
600 | 40911 | jldominguez | return false; |
601 | } |
||
602 | return true; |
||
603 | } |
||
604 | 41789 | fdiaz | |
605 | 42980 | fdiaz | private boolean isOverlapping(BufferedImage bi, Geometry lblgeom) { |
606 | 41789 | fdiaz | |
607 | 42980 | fdiaz | for (Iterator iterator = drawnGeometryLabels.iterator(); iterator.hasNext();) { |
608 | Geometry drawnGeometry = (Geometry) iterator.next(); |
||
609 | try {
|
||
610 | if (drawnGeometry.intersects(lblgeom)) {
|
||
611 | 42555 | fdiaz | return true; |
612 | } |
||
613 | 42980 | fdiaz | } catch (GeometryOperationNotSupportedException | GeometryOperationException e) {
|
614 | logger.warn("Can't check overlapping geometry");
|
||
615 | 42555 | fdiaz | } |
616 | } |
||
617 | 42980 | fdiaz | drawnGeometryLabels.add(lblgeom); |
618 | return false; |
||
619 | 40911 | jldominguez | |
620 | 42980 | fdiaz | } |
621 | |||
622 | 40911 | jldominguez | private boolean isOnePoint(ViewPort viewPort, Geometry geom) { |
623 | 41789 | fdiaz | |
624 | 40911 | jldominguez | boolean onePoint = false; |
625 | int shapeType = geom.getType();
|
||
626 | 41789 | fdiaz | |
627 | 40911 | jldominguez | if (shapeType != TYPES.POINT && shapeType != TYPES.MULTIPOINT) {
|
628 | |||
629 | Envelope env = geom.getEnvelope(); |
||
630 | double dist1Pixel = viewPort.getDist1pixel();
|
||
631 | 41789 | fdiaz | onePoint = (env.getLength(0) <= dist1Pixel && env.getLength(1) <= dist1Pixel); |
632 | 40911 | jldominguez | } |
633 | return onePoint;
|
||
634 | } |
||
635 | |||
636 | public boolean isAllowingOverlap() { |
||
637 | return allowOverlapping;
|
||
638 | } |
||
639 | |||
640 | public void setAllowOverlapping(boolean allowOverlapping) { |
||
641 | this.allowOverlapping = allowOverlapping;
|
||
642 | } |
||
643 | |||
644 | public IPlacementConstraints getPlacementConstraints() {
|
||
645 | if (placementConstraints != null) |
||
646 | return placementConstraints;
|
||
647 | |||
648 | GeometryType gt = null;
|
||
649 | 41789 | fdiaz | |
650 | 40911 | jldominguez | try {
|
651 | gt = layer.getGeometryType(); |
||
652 | // force 2d for comparison
|
||
653 | gt = GeometryLocator.getGeometryManager().getGeometryType( |
||
654 | gt.getType(), SUBTYPES.GEOM2D); |
||
655 | } catch (Exception e) { |
||
656 | logger.error("While getting placements constraints.", e);
|
||
657 | return null; |
||
658 | } |
||
659 | 41789 | fdiaz | |
660 | if (gt.isTypeOf(TYPES.POINT) || gt.isTypeOf(TYPES.MULTIPOINT)) {
|
||
661 | 40911 | jldominguez | return DefaultPointPlacementConstraints;
|
662 | } else {
|
||
663 | 41789 | fdiaz | if (gt.isTypeOf(TYPES.CURVE) || gt.isTypeOf(TYPES.MULTICURVE)) {
|
664 | 40911 | jldominguez | return DefaultLinePlacementConstraints;
|
665 | } else {
|
||
666 | if (gt.isTypeOf(TYPES.SURFACE)
|
||
667 | || gt.isTypeOf(TYPES.MULTISURFACE)) { |
||
668 | return DefaultPolygonPlacementConstraints;
|
||
669 | } else {
|
||
670 | 41789 | fdiaz | if (gt.isTypeOf(TYPES.AGGREGATE)
|
671 | || gt.isTypeOf(TYPES.GEOMETRY)) { |
||
672 | DefaultMultiShapePlacementConstratints |
||
673 | .setPointConstraints(DefaultPointPlacementConstraints); |
||
674 | DefaultMultiShapePlacementConstratints |
||
675 | .setLineConstraints(DefaultLinePlacementConstraints); |
||
676 | DefaultMultiShapePlacementConstratints |
||
677 | .setPolygonConstraints(DefaultPolygonPlacementConstraints); |
||
678 | 40911 | jldominguez | return DefaultMultiShapePlacementConstratints;
|
679 | } |
||
680 | } |
||
681 | } |
||
682 | } |
||
683 | return null; |
||
684 | } |
||
685 | |||
686 | public void setPlacementConstraints(IPlacementConstraints constraints) { |
||
687 | this.placementConstraints = constraints;
|
||
688 | } |
||
689 | |||
690 | public IZoomConstraints getZoomConstraints() {
|
||
691 | return zoomConstraints;
|
||
692 | } |
||
693 | |||
694 | public void setZoomConstraints(IZoomConstraints constraints) { |
||
695 | this.zoomConstraints = constraints;
|
||
696 | } |
||
697 | |||
698 | 41789 | fdiaz | public void print(Graphics2D g, double scale, ViewPort viewPort, |
699 | 40911 | jldominguez | Cancellable cancel, PrintAttributes properties) |
700 | throws ReadException {
|
||
701 | 41789 | fdiaz | |
702 | 40911 | jldominguez | double dpi = 100; |
703 | int pq = properties.getPrintQuality();
|
||
704 | 41789 | fdiaz | if (pq == PrintAttributes.PRINT_QUALITY_NORMAL) {
|
705 | 40911 | jldominguez | dpi = 300;
|
706 | 41789 | fdiaz | } else if (pq == PrintAttributes.PRINT_QUALITY_HIGH) { |
707 | 40911 | jldominguez | dpi = 600;
|
708 | 41789 | fdiaz | } else if (pq == PrintAttributes.PRINT_QUALITY_DRAFT) { |
709 | 40911 | jldominguez | dpi = 72;
|
710 | } |
||
711 | |||
712 | 41789 | fdiaz | viewPort.setOffset(new Point2D.Double(0, 0)); |
713 | |||
714 | 40911 | jldominguez | /* signal printing output */
|
715 | printMode = true;
|
||
716 | |||
717 | 41789 | fdiaz | draw(null, g, scale, viewPort, cancel, dpi);
|
718 | 40911 | jldominguez | } |
719 | |||
720 | public String[] getUsedFields() { |
||
721 | |||
722 | /*
|
||
723 | 41789 | fdiaz | * TODO Solve the problem with the [ and ]. Currently SQLJEP evaluator
|
724 | * cannot tell which fields are being used. Options: allow [] and remove
|
||
725 | * them or maybe while parsing the SQLJEP evaluator can inform with
|
||
726 | * events like "I have found a field"
|
||
727 | 40911 | jldominguez | */
|
728 | 41789 | fdiaz | |
729 | 40911 | jldominguez | FeatureAttributeDescriptor[] atts = null; |
730 | try {
|
||
731 | 41789 | fdiaz | atts = layer.getFeatureStore().getDefaultFeatureType() |
732 | .getAttributeDescriptors(); |
||
733 | 40911 | jldominguez | } catch (DataException e) {
|
734 | logger.error("While getting atributes.", e);
|
||
735 | } |
||
736 | 41789 | fdiaz | |
737 | 40911 | jldominguez | int n = atts.length;
|
738 | String[] resp = new String[n]; |
||
739 | 41789 | fdiaz | for (int i = 0; i < n; i++) { |
740 | 40911 | jldominguez | resp[i] = atts[i].getName(); |
741 | } |
||
742 | return resp;
|
||
743 | |||
744 | } |
||
745 | |||
746 | public boolean shouldDrawLabels(double scale) { |
||
747 | double minScaleView = -1; |
||
748 | double maxScaleView = -1; |
||
749 | |||
750 | if (zoomConstraints != null) { |
||
751 | minScaleView = zoomConstraints.getMinScale(); |
||
752 | maxScaleView = zoomConstraints.getMaxScale(); |
||
753 | } |
||
754 | |||
755 | if (minScaleView == -1 && maxScaleView == -1) { |
||
756 | // parameters not set, so the layer decides.
|
||
757 | return layer.isWithinScale(scale);
|
||
758 | } |
||
759 | |||
760 | if (minScaleView >= scale) {
|
||
761 | return (maxScaleView != -1) ? maxScaleView <= scale : true; |
||
762 | } |
||
763 | |||
764 | return false; |
||
765 | } |
||
766 | |||
767 | public void setUnit(int unitIndex) { |
||
768 | unit = unitIndex; |
||
769 | |||
770 | } |
||
771 | |||
772 | public int getUnit() { |
||
773 | return unit;
|
||
774 | } |
||
775 | |||
776 | public int getReferenceSystem() { |
||
777 | return referenceSystem;
|
||
778 | } |
||
779 | |||
780 | public void setReferenceSystem(int referenceSystem) { |
||
781 | this.referenceSystem = referenceSystem;
|
||
782 | } |
||
783 | 41789 | fdiaz | |
784 | 40911 | jldominguez | public static void registerPersistent() { |
785 | 41789 | fdiaz | |
786 | PersistenceManager manager = ToolsLocator.getPersistenceManager(); |
||
787 | if (manager.getDefinition(GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME) == null) { |
||
788 | DynStruct definition = manager.addDefinition( |
||
789 | GeneralLabelingStrategy.class, |
||
790 | GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME, |
||
791 | GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME |
||
792 | + " Persistence definition", null, null); |
||
793 | definition.addDynFieldObject("labelingMethod")
|
||
794 | .setClassOfValue(ILabelingMethod.class).setMandatory(true);
|
||
795 | definition.addDynFieldObject("placementConstraints")
|
||
796 | .setClassOfValue(IPlacementConstraints.class) |
||
797 | .setMandatory(false);
|
||
798 | definition.addDynFieldObject("zoomConstraints")
|
||
799 | .setClassOfValue(IZoomConstraints.class) |
||
800 | .setMandatory(false);
|
||
801 | |||
802 | definition.addDynFieldBoolean("allowOverlapping")
|
||
803 | .setMandatory(true);
|
||
804 | definition.addDynFieldInt("unit").setMandatory(true); |
||
805 | definition.addDynFieldInt("referenceSystem").setMandatory(true); |
||
806 | } |
||
807 | 40911 | jldominguez | } |
808 | 41789 | fdiaz | |
809 | public void loadFromState(PersistentState state) |
||
810 | throws PersistenceException {
|
||
811 | |||
812 | 40911 | jldominguez | method = (ILabelingMethod) state.get("labelingMethod");
|
813 | 41789 | fdiaz | |
814 | 40911 | jldominguez | if (state.hasValue("placementConstraints")) { |
815 | 41789 | fdiaz | placementConstraints = (IPlacementConstraints) state |
816 | .get("placementConstraints");
|
||
817 | 40911 | jldominguez | } |
818 | 41789 | fdiaz | |
819 | 40911 | jldominguez | if (state.hasValue("zoomConstraints")) { |
820 | zoomConstraints = (IZoomConstraints) state.get("zoomConstraints");
|
||
821 | } |
||
822 | |||
823 | this.allowOverlapping = state.getBoolean("allowOverlapping"); |
||
824 | this.unit = state.getInt("unit"); |
||
825 | this.referenceSystem = state.getInt("referenceSystem"); |
||
826 | } |
||
827 | |||
828 | public void saveToState(PersistentState state) throws PersistenceException { |
||
829 | 41789 | fdiaz | |
830 | 40911 | jldominguez | state.set("labelingMethod", method);
|
831 | 41789 | fdiaz | |
832 | 40911 | jldominguez | if (placementConstraints != null) { |
833 | state.set("placementConstraints", placementConstraints);
|
||
834 | } |
||
835 | |||
836 | if (zoomConstraints != null) { |
||
837 | state.set("zoomConstraints", zoomConstraints);
|
||
838 | } |
||
839 | |||
840 | state.set("allowOverlapping", allowOverlapping);
|
||
841 | state.set("unit", unit);
|
||
842 | state.set("referenceSystem", referenceSystem);
|
||
843 | |||
844 | } |
||
845 | |||
846 | public double toCartographicSize(ViewPort vp, double dpi, Geometry geom) { |
||
847 | /*
|
||
848 | * This method is not used but we must implement CartographicSupport
|
||
849 | */
|
||
850 | return 0; |
||
851 | } |
||
852 | |||
853 | public void setCartographicSize(double cartographicSize, Geometry geom) { |
||
854 | /*
|
||
855 | * This method is not used but we must implement CartographicSupport
|
||
856 | */
|
||
857 | } |
||
858 | |||
859 | public double getCartographicSize(ViewPort vp, double dpi, Geometry geom) { |
||
860 | /*
|
||
861 | * This method is not used but we must implement CartographicSupport
|
||
862 | */
|
||
863 | return 0; |
||
864 | } |
||
865 | 41789 | fdiaz | |
866 | 40911 | jldominguez | public Object clone() throws CloneNotSupportedException { |
867 | return LabelClassUtils.clone(this); |
||
868 | } |
||
869 | |||
870 | } |