svn-gvsig-desktop / tags / v1_1_Build_1007 / libraries / libFMap / src / com / iver / cit / gvsig / fmap / core / v02 / FGraphicUtilities.java @ 12478
History | View | Annotate | Download (24.4 KB)
1 | 452 | fjp | /*
|
---|---|---|---|
2 | * Created on 28-abr-2004
|
||
3 | *
|
||
4 | * To change the template for this generated file go to
|
||
5 | * Window>Preferences>Java>Code Generation>Code and Comments
|
||
6 | */
|
||
7 | 1100 | fjp | /* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
|
8 | *
|
||
9 | * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
|
||
10 | *
|
||
11 | * This program is free software; you can redistribute it and/or
|
||
12 | * modify it under the terms of the GNU General Public License
|
||
13 | * as published by the Free Software Foundation; either version 2
|
||
14 | * of the License, or (at your option) any later version.
|
||
15 | *
|
||
16 | * This program is distributed in the hope that it will be useful,
|
||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
19 | * GNU General Public License for more details.
|
||
20 | *
|
||
21 | * You should have received a copy of the GNU General Public License
|
||
22 | * along with this program; if not, write to the Free Software
|
||
23 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
|
||
24 | *
|
||
25 | * For more information, contact:
|
||
26 | *
|
||
27 | * Generalitat Valenciana
|
||
28 | * Conselleria d'Infraestructures i Transport
|
||
29 | * Av. Blasco Ib??ez, 50
|
||
30 | * 46010 VALENCIA
|
||
31 | * SPAIN
|
||
32 | *
|
||
33 | * +34 963862235
|
||
34 | * gvsig@gva.es
|
||
35 | * www.gvsig.gva.es
|
||
36 | *
|
||
37 | * or
|
||
38 | *
|
||
39 | * IVER T.I. S.A
|
||
40 | * Salamanca 50
|
||
41 | * 46005 Valencia
|
||
42 | * Spain
|
||
43 | *
|
||
44 | * +34 963163400
|
||
45 | * dac@iver.es
|
||
46 | */
|
||
47 | 452 | fjp | package com.iver.cit.gvsig.fmap.core.v02; |
48 | |||
49 | 4213 | caballero | import java.awt.Color; |
50 | import java.awt.Font; |
||
51 | import java.awt.FontMetrics; |
||
52 | import java.awt.Graphics2D; |
||
53 | import java.awt.Rectangle; |
||
54 | import java.awt.geom.AffineTransform; |
||
55 | import java.awt.geom.Point2D; |
||
56 | |||
57 | import org.apache.batik.ext.awt.geom.PathLength; |
||
58 | |||
59 | 452 | fjp | import com.iver.cit.gvsig.fmap.core.FPoint2D; |
60 | import com.iver.cit.gvsig.fmap.core.FPolygon2D; |
||
61 | import com.iver.cit.gvsig.fmap.core.FPolyline2D; |
||
62 | import com.iver.cit.gvsig.fmap.core.FShape; |
||
63 | import com.iver.cit.gvsig.fmap.core.GeneralPathX; |
||
64 | 3833 | caballero | import com.iver.cit.gvsig.fmap.core.Handler; |
65 | 8765 | jjdelcerro | import com.iver.cit.gvsig.fmap.core.ISymbol; |
66 | 452 | fjp | import com.vividsolutions.jts.geom.Geometry; |
67 | import com.vividsolutions.jts.geom.Point; |
||
68 | |||
69 | |||
70 | /**
|
||
71 | 1005 | vcaballero | * Clase con m?todos est?ticos para dibujar sobre el Graphics que se les pase
|
72 | * como par?metro.
|
||
73 | 11434 | caballero | *
|
74 | 8765 | jjdelcerro | * Esta clase deber?a ser privada. Las clases que la usan son GraphicLayer
|
75 | * y AnnotationStrategy, pero hay que revisarlas para que no sea necesario.
|
||
76 | * Lo m?s urgente ser?a lo del dibujado de textos, para que sea
|
||
77 | * Hay que quitar las dependecias de FSymbol, y trabajar SIEMPRE con ISymbol.
|
||
78 | * Recordar: Que sea ISymbol el que renderiza.
|
||
79 | * extensible el s?mbolo a usar. NOTA: Ver tambi?n comentario en ISymbol
|
||
80 | 452 | fjp | *
|
81 | 1005 | vcaballero | * @author fjp
|
82 | 452 | fjp | */
|
83 | public class FGraphicUtilities { |
||
84 | 4256 | caballero | |
85 | |||
86 | 1005 | vcaballero | /**
|
87 | * Dibuja el s?mbolo que se le pasa como par?metro en el Graphics.
|
||
88 | *
|
||
89 | * @param g2 Graphics2D sobre el que dibujar.
|
||
90 | * @param mT2 Matriz de transformaci?n.
|
||
91 | * @param r Rect?ngulo.
|
||
92 | * @param symbol S?mbolo a dibujar.
|
||
93 | */
|
||
94 | public static void DrawSymbol(Graphics2D g2, AffineTransform mT2, |
||
95 | Rectangle r, FSymbol symbol) {
|
||
96 | FShape shp; |
||
97 | 452 | fjp | |
98 | 1005 | vcaballero | AffineTransform mT = new AffineTransform(); |
99 | mT.setToIdentity(); |
||
100 | 452 | fjp | |
101 | 1005 | vcaballero | Rectangle r2 = new Rectangle(r.x + 2 + (r.width / 3), r.y, r.width / 3, |
102 | r.height); |
||
103 | Rectangle r3 = new Rectangle(r.x + 2 + ((2 * r.width) / 3), r.y, |
||
104 | r.width / 3, r.height);
|
||
105 | 452 | fjp | |
106 | 1005 | vcaballero | // g2.clearRect(r.x, r.y, r.width, r.height);
|
107 | // System.out.println("r = " + r.toString() + " Color preview:" + symbol.m_Color.toString());
|
||
108 | // System.out.println("symbol.m_symbolType= "+symbol.m_symbolType);
|
||
109 | switch (symbol.getSymbolType()) {
|
||
110 | case FConstant.SYMBOL_TYPE_POINT:
|
||
111 | shp = new FPoint2D(r.x + (r.width / 2), r.y + (r.height / 2)); |
||
112 | 452 | fjp | |
113 | 1005 | vcaballero | // Para no tener que clonarlo si viene en unidades de mapa
|
114 | boolean bAux2 = symbol.isSizeInPixels();
|
||
115 | int alturaMetros = symbol.getSize(); // Nota: Cambiar m_Size a float |
||
116 | 452 | fjp | |
117 | 1005 | vcaballero | if (!bAux2) {
|
118 | symbol.setSizeInPixels(true);
|
||
119 | symbol.setSize(8); // tama?o fijo |
||
120 | } |
||
121 | 452 | fjp | |
122 | 8765 | jjdelcerro | symbol.draw(g2, mT, shp); |
123 | // FGraphicUtilities.DrawShape(g2, mT, shp, symbol);
|
||
124 | 452 | fjp | |
125 | 11434 | caballero | |
126 | 1005 | vcaballero | if (!bAux2) {
|
127 | symbol.setSize(alturaMetros); |
||
128 | symbol.setSizeInPixels(bAux2); |
||
129 | } |
||
130 | 452 | fjp | |
131 | 1005 | vcaballero | if (symbol.getFont() != null) { |
132 | // Para no tener que clonarlo si viene en unidades de mapa
|
||
133 | boolean bAux = symbol.isFontSizeInPixels();
|
||
134 | symbol.setFontSizeInPixels(true);
|
||
135 | FGraphicUtilities.DrawLabel(g2, mT, shp, symbol, |
||
136 | new FLabel("Abcd")); |
||
137 | symbol.setFontSizeInPixels(bAux); |
||
138 | } |
||
139 | 452 | fjp | |
140 | 1005 | vcaballero | break;
|
141 | 452 | fjp | |
142 | 1005 | vcaballero | case FConstant.SYMBOL_TYPE_LINE:
|
143 | 452 | fjp | |
144 | 1005 | vcaballero | Rectangle rect = mT2.createTransformedShape(r).getBounds();
|
145 | GeneralPathX line = new GeneralPathX();
|
||
146 | line.moveTo(rect.x, rect.y + (rect.height / 2));
|
||
147 | 452 | fjp | |
148 | 3748 | caballero | // line.lineTo(rect.x + rect.width/3, rect.y + rect.height);
|
149 | // line.lineTo(rect.x + 2*rect.width/3, rect.y);
|
||
150 | 1005 | vcaballero | // line.lineTo(rect.x + rect.width, rect.y + rect.height/2);
|
151 | line.curveTo(rect.x + (rect.width / 3),
|
||
152 | rect.y + (2 * rect.height),
|
||
153 | rect.x + ((2 * rect.width) / 3), rect.y - rect.height, |
||
154 | rect.x + rect.width, rect.y + (rect.height / 2));
|
||
155 | 452 | fjp | |
156 | 1005 | vcaballero | shp = new FPolyline2D(line);
|
157 | 8765 | jjdelcerro | symbol.draw(g2, mT, shp); |
158 | // FGraphicUtilities.DrawShape(g2, mT, shp, symbol);
|
||
159 | 452 | fjp | |
160 | 1005 | vcaballero | break;
|
161 | 452 | fjp | |
162 | 1005 | vcaballero | case FConstant.SYMBOL_TYPE_FILL:
|
163 | 810 | fjp | |
164 | 1005 | vcaballero | GeneralPathX rectAux = new GeneralPathX(r);
|
165 | rectAux.transform(mT2); |
||
166 | shp = new FPolygon2D(rectAux);
|
||
167 | 810 | fjp | |
168 | 1005 | vcaballero | // System.out.println("rect = "+rectAux.getBounds());
|
169 | 8765 | jjdelcerro | symbol.draw(g2, mT, shp); |
170 | // FGraphicUtilities.DrawShape(g2, mT, shp, symbol);
|
||
171 | 810 | fjp | |
172 | 1005 | vcaballero | break;
|
173 | 843 | fjp | |
174 | 1005 | vcaballero | case FShape.MULTI:
|
175 | 843 | fjp | |
176 | 1005 | vcaballero | // Pol?gono
|
177 | r.resize(r.width / 3, r.height);
|
||
178 | 843 | fjp | |
179 | 1005 | vcaballero | GeneralPathX rectAux2 = new GeneralPathX(r);
|
180 | rectAux2.transform(mT2); |
||
181 | shp = new FPolygon2D(rectAux2);
|
||
182 | 8765 | jjdelcerro | symbol.draw(g2, mT, shp); |
183 | // FGraphicUtilities.DrawShape(g2, mT, shp, symbol);
|
||
184 | 843 | fjp | |
185 | 1005 | vcaballero | // L?nea
|
186 | rect = mT2.createTransformedShape(r2).getBounds(); |
||
187 | line = new GeneralPathX();
|
||
188 | line.moveTo(rect.x, rect.y + (rect.height / 2));
|
||
189 | 843 | fjp | |
190 | 1005 | vcaballero | line.curveTo(rect.x + (rect.width / 3),
|
191 | rect.y + (2 * rect.height),
|
||
192 | rect.x + ((2 * rect.width) / 3), rect.y - rect.height, |
||
193 | rect.x + rect.width, rect.y + (rect.height / 2));
|
||
194 | 843 | fjp | |
195 | 1005 | vcaballero | shp = new FPolyline2D(line);
|
196 | 8765 | jjdelcerro | symbol.draw(g2, mT, shp); |
197 | // FGraphicUtilities.DrawShape(g2, mT, shp, symbol);
|
||
198 | 843 | fjp | |
199 | 1005 | vcaballero | // Punto:
|
200 | shp = new FPoint2D(r3.x + (r3.width / 2), r3.y + |
||
201 | (r3.height / 2));
|
||
202 | 452 | fjp | |
203 | 1005 | vcaballero | // Para no tener que clonarlo si viene en unidades de mapa
|
204 | bAux2 = symbol.isSizeInPixels(); |
||
205 | alturaMetros = symbol.getSize(); // Nota: Cambiar m_Size a float
|
||
206 | 452 | fjp | |
207 | 1005 | vcaballero | if (!bAux2) {
|
208 | symbol.setSizeInPixels(true);
|
||
209 | symbol.setSize(4); // tama?o fijo |
||
210 | } |
||
211 | 8765 | jjdelcerro | symbol.draw(g2, mT, shp); |
212 | // FGraphicUtilities.DrawShape(g2, mT, shp, symbol);
|
||
213 | 452 | fjp | |
214 | 1005 | vcaballero | if (!bAux2) {
|
215 | symbol.setSize(alturaMetros); |
||
216 | symbol.setSizeInPixels(bAux2); |
||
217 | } |
||
218 | 452 | fjp | |
219 | 1005 | vcaballero | if (symbol.getFont() != null) { |
220 | // Para no tener que clonarlo si viene en unidades de mapa
|
||
221 | boolean bAux = symbol.isFontSizeInPixels();
|
||
222 | symbol.setFontSizeInPixels(true);
|
||
223 | FGraphicUtilities.DrawLabel(g2, mT, shp, symbol, |
||
224 | new FLabel("Abcd")); |
||
225 | symbol.setFontSizeInPixels(bAux); |
||
226 | } |
||
227 | 452 | fjp | |
228 | 1005 | vcaballero | break;
|
229 | 4296 | caballero | case FConstant.SYMBOL_TYPE_TEXT:
|
230 | shp = new FPoint2D(r3.x + (r3.width / 2), r3.y + |
||
231 | (r3.height / 2));
|
||
232 | boolean bAux = symbol.isFontSizeInPixels();
|
||
233 | symbol.setFontSizeInPixels(true);
|
||
234 | FGraphicUtilities.DrawLabel(g2, mT, shp, symbol, |
||
235 | new FLabel("Abcd")); |
||
236 | symbol.setFontSizeInPixels(bAux); |
||
237 | break;
|
||
238 | 1005 | vcaballero | } |
239 | } |
||
240 | 452 | fjp | |
241 | 1005 | vcaballero | /**
|
242 | * Dibuja el shape que se pasa como par?metro con las caracter?sticas que
|
||
243 | * aporta el s?mbolo sobre el Graphics2D.
|
||
244 | *
|
||
245 | * @param g2 Graphics2D sobre el que dibujar.
|
||
246 | * @param mT Matriz de transformaci?n.
|
||
247 | * @param shp FShape a dibujar.
|
||
248 | * @param theSymbol S?mbolo.
|
||
249 | */
|
||
250 | public static void DrawShape(Graphics2D g2, AffineTransform mT, FShape shp, |
||
251 | FSymbol theSymbol) { |
||
252 | // Hacemos la transformaci?n del shape aqu? dentro... por ahora.
|
||
253 | 3035 | fjp | if (shp == null || theSymbol == null || (!theSymbol.isShapeVisible())) { |
254 | 1005 | vcaballero | return;
|
255 | } |
||
256 | 1402 | fjp | g2.setColor(theSymbol.getColor()); |
257 | 452 | fjp | |
258 | 1005 | vcaballero | /* if (shp instanceof FPolygon2D)
|
259 | {
|
||
260 | System.out.println("Entra pol?gono");
|
||
261 | } */
|
||
262 | 2196 | vcaballero | int type=shp.getShapeType();
|
263 | 2354 | fjp | /* if (shp.getShapeType()>=FShape.Z){
|
264 | 2196 | vcaballero | type=shp.getShapeType()-FShape.Z;
|
265 | 2354 | fjp | } */
|
266 | 2196 | vcaballero | switch (type) {
|
267 | 1005 | vcaballero | case FShape.POINT: //Tipo punto |
268 | 2354 | fjp | case FShape.POINT + FShape.Z:
|
269 | 1005 | vcaballero | drawSymbolPoint(g2, mT, (FPoint2D) shp, theSymbol); |
270 | 452 | fjp | |
271 | 1005 | vcaballero | break;
|
272 | 452 | fjp | |
273 | 1005 | vcaballero | case FShape.LINE:
|
274 | 2354 | fjp | case FShape.LINE + FShape.Z:
|
275 | 3783 | caballero | case FShape.ARC:
|
276 | case FShape.ARC + FShape.Z:
|
||
277 | 1005 | vcaballero | // Shape theShp = mT.createTransformedShape(shp.m_Polyline);
|
278 | // g2.setColor(theSymbol.m_Color);
|
||
279 | if (theSymbol.getStroke() != null) { |
||
280 | g2.setStroke(theSymbol.getStroke()); |
||
281 | } |
||
282 | 452 | fjp | |
283 | 1005 | vcaballero | g2.draw(shp); |
284 | 452 | fjp | |
285 | 1005 | vcaballero | break;
|
286 | 452 | fjp | |
287 | 1005 | vcaballero | case FShape.POLYGON:
|
288 | 2354 | fjp | case FShape.POLYGON + FShape.Z:
|
289 | 6013 | fjp | case FShape.ELLIPSE:
|
290 | case FShape.ELLIPSE + FShape.Z:
|
||
291 | case FShape.CIRCLE:
|
||
292 | case FShape.CIRCLE + FShape.Z:
|
||
293 | 11434 | caballero | |
294 | 1484 | fjp | if (theSymbol.getFill() != null) |
295 | g2.setPaint(theSymbol.getFill()); |
||
296 | |||
297 | 3748 | caballero | if (theSymbol.getColor() != null) |
298 | 1402 | fjp | if (theSymbol.getStyle() != FConstant.SYMBOL_STYLE_DGNSPECIAL) {
|
299 | g2.fill(shp); |
||
300 | 3748 | caballero | } |
301 | 452 | fjp | |
302 | 1005 | vcaballero | if (theSymbol.isOutlined()) {
|
303 | g2.setColor(theSymbol.getOutlineColor()); |
||
304 | 452 | fjp | |
305 | 1005 | vcaballero | if (theSymbol.getStroke() != null) { |
306 | g2.setStroke(theSymbol.getStroke()); |
||
307 | } |
||
308 | 452 | fjp | |
309 | 1005 | vcaballero | g2.draw(shp); |
310 | } |
||
311 | 452 | fjp | |
312 | 1005 | vcaballero | break;
|
313 | 3748 | caballero | |
314 | 1005 | vcaballero | } |
315 | } |
||
316 | 452 | fjp | |
317 | 1005 | vcaballero | /**
|
318 | * Dibuja el FLabel que se pasa como par?metro sobre el Graphics2D.
|
||
319 | *
|
||
320 | * @param g2 Graphics2D sobre el que dibujar.
|
||
321 | * @param mT Matriz de transformaci?n.
|
||
322 | * @param shp FShape a dibujar.
|
||
323 | * @param theSymbol S?mbolo para aplicar.
|
||
324 | * @param theLabel FLabel que contiene el texto que se debe dibujar.
|
||
325 | */
|
||
326 | public static void DrawLabel(Graphics2D g2, AffineTransform mT, FShape shp, |
||
327 | FSymbol theSymbol, FLabel theLabel) { |
||
328 | float angle;
|
||
329 | float x;
|
||
330 | float y;
|
||
331 | Point2D pAux = null; |
||
332 | 452 | fjp | |
333 | 1005 | vcaballero | // USAR TEXTLAYOUT SI QUEREMOS PERMITIR SELECCIONAR UN TEXTO
|
334 | // Y/O EDITARLO "IN SITU"
|
||
335 | 452 | fjp | |
336 | 1005 | vcaballero | /* if (m_labelValues[numReg].length() > 0)
|
337 | {
|
||
338 | TextLayout layout = new TextLayout(m_labelValues[numReg], font, frc);
|
||
339 | layout.draw(g2, x, y);
|
||
340 | } */
|
||
341 | if (shp == null) { |
||
342 | return;
|
||
343 | } |
||
344 | 452 | fjp | |
345 | 1005 | vcaballero | // Las etiquetas que pongamos a nulo ser? porque no la queremos dibujar.
|
346 | // ?til para cuando queramos eliminar duplicados.
|
||
347 | if (theLabel.getString() == null) { |
||
348 | return;
|
||
349 | } |
||
350 | 452 | fjp | |
351 | 1005 | vcaballero | FontMetrics metrics = g2.getFontMetrics();
|
352 | int width = metrics.stringWidth(theLabel.getString());
|
||
353 | int height = metrics.getMaxAscent();
|
||
354 | 452 | fjp | |
355 | 1005 | vcaballero | // int height = metrics.getHeight();
|
356 | g2.setFont(theSymbol.getFont()); |
||
357 | g2.setColor(theSymbol.getFontColor()); |
||
358 | 452 | fjp | |
359 | 1005 | vcaballero | // Aqu? hay que mirar m_Size y m_useSize...
|
360 | if (!theSymbol.isFontSizeInPixels()) {
|
||
361 | // Suponemos que m_Size viene en coordenadas de mundo real
|
||
362 | // Esto habr? que cambiarlo. Probablemente usar Style2d de geotools en lugar
|
||
363 | // de FSymbol.
|
||
364 | // CAMBIO: La altura del texto la miramos en FLabel
|
||
365 | // float alturaPixels = (float) (theSymbol.m_FontSize * mT.getScaleX());
|
||
366 | float alturaPixels = (float) (theLabel.getHeight() * mT.getScaleX()); |
||
367 | 452 | fjp | |
368 | 1005 | vcaballero | /* System.out.println("m_bUseSize = " + theSymbol.m_bUseSize +
|
369 | " Escala: " + mT.getScaleX() + " alturaPixels = " + alturaPixels); */
|
||
370 | if (alturaPixels < 3) { |
||
371 | return; // No leemos nada |
||
372 | } |
||
373 | 452 | fjp | |
374 | 1005 | vcaballero | Font nuevaFuente = theSymbol.getFont().deriveFont(alturaPixels);
|
375 | g2.setFont(nuevaFuente); |
||
376 | width = g2.getFontMetrics().stringWidth(theLabel.getString()); |
||
377 | } |
||
378 | 452 | fjp | |
379 | 2196 | vcaballero | int type=shp.getShapeType();
|
380 | if (shp.getShapeType()>=FShape.Z){
|
||
381 | type=shp.getShapeType()-FShape.Z; |
||
382 | } |
||
383 | switch (type) {
|
||
384 | 1005 | vcaballero | case FShape.POINT: //Tipo punto |
385 | pAux = new Point2D.Double(((FPoint2D) shp).getX(), |
||
386 | ((FPoint2D) shp).getY()); |
||
387 | pAux = mT.transform(pAux, null);
|
||
388 | 452 | fjp | |
389 | 1005 | vcaballero | break;
|
390 | 452 | fjp | |
391 | 1005 | vcaballero | case FShape.LINE:
|
392 | 452 | fjp | |
393 | 3748 | caballero | //
|
394 | 1005 | vcaballero | if (theLabel.getOrig() == null) // Calculamos el punto y la orientaci?n solo la primera vez |
395 | { |
||
396 | PathLength pathLen = new PathLength(shp);
|
||
397 | 452 | fjp | |
398 | 1005 | vcaballero | // if (pathLen.lengthOfPath() < width / mT.getScaleX()) return;
|
399 | float midDistance = pathLen.lengthOfPath() / 2; |
||
400 | pAux = pathLen.pointAtLength(midDistance); |
||
401 | angle = pathLen.angleAtLength(midDistance); |
||
402 | 452 | fjp | |
403 | 1005 | vcaballero | if (angle < 0) { |
404 | angle = angle + (float) (2 * Math.PI); |
||
405 | } |
||
406 | 452 | fjp | |
407 | 1005 | vcaballero | if ((angle > (Math.PI / 2)) && |
408 | (angle < ((3 * Math.PI) / 2))) { |
||
409 | angle = angle - (float) Math.PI; |
||
410 | } |
||
411 | 452 | fjp | |
412 | 1005 | vcaballero | theLabel.setRotation(Math.toDegrees(angle));
|
413 | theLabel.setOrig(pAux); |
||
414 | } |
||
415 | 452 | fjp | |
416 | 1005 | vcaballero | pAux = mT.transform(theLabel.getOrig(), null);
|
417 | 452 | fjp | |
418 | 1005 | vcaballero | // pAux = theLabel.getOrig();
|
419 | // GlyphVector theGlyphs = theSymbol.m_Font.createGlyphVector(g2.getFontRenderContext(), theLabel);
|
||
420 | 3748 | caballero | // Shape txtShp = TextPathLayout.layoutGlyphVector(theGlyphs, shp.m_Polyline,TextPathLayout.ALIGN_MIDDLE);
|
421 | 1005 | vcaballero | // g2.draw(txtShp);
|
422 | // System.out.println("Pintando etiqueta " + theLabel );
|
||
423 | break;
|
||
424 | 452 | fjp | |
425 | 1005 | vcaballero | case FShape.POLYGON:
|
426 | 452 | fjp | |
427 | 1005 | vcaballero | if (theLabel.getOrig() == null) // Calculamos el punto solo la primera vez |
428 | { |
||
429 | Geometry geo = FConverter.java2d_to_jts(shp); |
||
430 | 452 | fjp | |
431 | 1005 | vcaballero | // System.out.println("Area de " + m_labelValues[numReg] + " = "
|
432 | // + geo.getArea());
|
||
433 | 3748 | caballero | // System.out.println(geo.toText());
|
434 | 1005 | vcaballero | Point pJTS = geo.getInteriorPoint();
|
435 | FShape pLabel = FConverter.jts_to_java2d(pJTS); |
||
436 | theLabel.setRotation(0);
|
||
437 | theLabel.setOrig(new Point2D.Double( |
||
438 | ((FPoint2D) pLabel).getX(), |
||
439 | ((FPoint2D) pLabel).getX())); |
||
440 | } |
||
441 | 452 | fjp | |
442 | 1005 | vcaballero | pAux = mT.transform(theLabel.getOrig(), null);
|
443 | 452 | fjp | |
444 | 1005 | vcaballero | break;
|
445 | } |
||
446 | 452 | fjp | |
447 | 1005 | vcaballero | AffineTransform ant = g2.getTransform();
|
448 | 452 | fjp | |
449 | 1005 | vcaballero | x = (float) pAux.getX();
|
450 | y = (float) pAux.getY();
|
||
451 | 452 | fjp | |
452 | 1005 | vcaballero | AffineTransform Tx = (AffineTransform) ant.clone(); |
453 | Tx.translate(x, y); // S3: final translation
|
||
454 | 4226 | caballero | Tx.rotate(theLabel.getRotation()); // S2: rotate around anchor
|
455 | 1005 | vcaballero | g2.setTransform(Tx); |
456 | 452 | fjp | |
457 | 1005 | vcaballero | switch (theLabel.getJustification()) {
|
458 | case FLabel.LEFT_BOTTOM:
|
||
459 | g2.drawString(theLabel.getString(), 0, 0 - 3); |
||
460 | 452 | fjp | |
461 | 1005 | vcaballero | break;
|
462 | 452 | fjp | |
463 | 1005 | vcaballero | case FLabel.LEFT_CENTER:
|
464 | g2.drawString(theLabel.getString(), 0, 0 - (height / 2)); |
||
465 | 452 | fjp | |
466 | 1005 | vcaballero | break;
|
467 | 452 | fjp | |
468 | 1005 | vcaballero | case FLabel.LEFT_TOP:
|
469 | g2.drawString(theLabel.getString(), 0, 0 - height); |
||
470 | 452 | fjp | |
471 | 1005 | vcaballero | break;
|
472 | 452 | fjp | |
473 | 1005 | vcaballero | case FLabel.CENTER_BOTTOM:
|
474 | g2.drawString(theLabel.getString(), 0 - (width / 2), 0 - 3); |
||
475 | 452 | fjp | |
476 | 1005 | vcaballero | break;
|
477 | 452 | fjp | |
478 | 1005 | vcaballero | case FLabel.CENTER_CENTER:
|
479 | g2.drawString(theLabel.getString(), 0 - (width / 2), |
||
480 | 0 - (height / 2)); |
||
481 | 452 | fjp | |
482 | 1005 | vcaballero | break;
|
483 | 452 | fjp | |
484 | 1005 | vcaballero | case FLabel.CENTER_TOP:
|
485 | g2.drawString(theLabel.getString(), 0 - (width / 2), 0 - |
||
486 | height); |
||
487 | 452 | fjp | |
488 | 1005 | vcaballero | break;
|
489 | |||
490 | case FLabel.RIGHT_BOTTOM:
|
||
491 | g2.drawString(theLabel.getString(), 0 - width, 0 - 3); |
||
492 | |||
493 | break;
|
||
494 | |||
495 | case FLabel.RIGHT_CENTER:
|
||
496 | g2.drawString(theLabel.getString(), 0 - width, 0 - |
||
497 | (height / 2));
|
||
498 | |||
499 | break;
|
||
500 | |||
501 | case FLabel.RIGHT_TOP:
|
||
502 | g2.drawString(theLabel.getString(), 0 - width, 0 - height); |
||
503 | |||
504 | break;
|
||
505 | } |
||
506 | |||
507 | // Restauramos
|
||
508 | g2.setTransform(ant); |
||
509 | } |
||
510 | 4213 | caballero | /**
|
511 | * Dibuja el FLabel que se pasa como par?metro sobre el Graphics2D.
|
||
512 | *
|
||
513 | * @param g2 Graphics2D sobre el que dibujar.
|
||
514 | * @param mT Matriz de transformaci?n.
|
||
515 | * @param shp FShape a dibujar.
|
||
516 | * @param theSymbol S?mbolo para aplicar.
|
||
517 | * @param theLabel FLabel que contiene el texto que se debe dibujar.
|
||
518 | */
|
||
519 | 4256 | caballero | public static void DrawAnnotation(Graphics2D g2, AffineTransform at, |
520 | FSymbol theSymbol, FLabel theLabel,FontMetrics metrics,boolean isSelected) { |
||
521 | 4213 | caballero | float x;
|
522 | float y;
|
||
523 | Point2D pAux = null; |
||
524 | // Las etiquetas que pongamos a nulo ser? porque no la queremos dibujar.
|
||
525 | // ?til para cuando queramos eliminar duplicados.
|
||
526 | if (theLabel.getString() == null) { |
||
527 | return;
|
||
528 | } |
||
529 | |||
530 | 4296 | caballero | // Aqu? hay que mirar m_Size y m_useSize...
|
531 | if (!theSymbol.isFontSizeInPixels()) {
|
||
532 | 4213 | caballero | // Suponemos que m_Size viene en coordenadas de mundo real
|
533 | // Esto habr? que cambiarlo. Probablemente usar Style2d de geotools en lugar
|
||
534 | // de FSymbol.
|
||
535 | // CAMBIO: La altura del texto la miramos en FLabel
|
||
536 | // float alturaPixels = (float) (theSymbol.m_FontSize * mT.getScaleX());
|
||
537 | 11547 | caballero | float alturaPixels = (float) (theLabel.getHeight() * at.getScaleX()); |
538 | 4213 | caballero | if (alturaPixels < 3) { |
539 | return; // No leemos nada |
||
540 | } |
||
541 | 4296 | caballero | Font nuevaFuente = theSymbol.getFont().deriveFont(alturaPixels);
|
542 | 4213 | caballero | g2.setFont(nuevaFuente); |
543 | } |
||
544 | 11547 | caballero | else{
|
545 | g2.setFont(theSymbol.getFont()); |
||
546 | } |
||
547 | 4213 | caballero | |
548 | 4296 | caballero | |
549 | if (isSelected){
|
||
550 | g2.setColor(FSymbol.getSelectionColor()); |
||
551 | }else{
|
||
552 | g2.setColor(theSymbol.getFontColor()); |
||
553 | } |
||
554 | pAux = at.transform(theLabel.getOrig(), null);
|
||
555 | 4213 | caballero | AffineTransform ant = g2.getTransform();
|
556 | |||
557 | x = (float) pAux.getX();
|
||
558 | y = (float) pAux.getY();
|
||
559 | |||
560 | AffineTransform Tx = (AffineTransform) ant.clone(); |
||
561 | Tx.translate(x, y); // S3: final translation
|
||
562 | 4226 | caballero | Tx.rotate(theLabel.getRotation()); // S2: rotate around anchor
|
563 | 4213 | caballero | g2.setTransform(Tx); |
564 | |||
565 | 4256 | caballero | |
566 | String s=theLabel.getString();
|
||
567 | |||
568 | |||
569 | 5511 | caballero | //switch (theLabel.getJustification()) {
|
570 | 4213 | caballero | |
571 | 5511 | caballero | //case FLabel.LEFT_BOTTOM:
|
572 | 4226 | caballero | g2.drawString(s, 0, 0 - 3); |
573 | 5511 | caballero | /*
|
574 | 4213 | caballero | break;
|
575 | 4226 | caballero | |
576 | 4213 | caballero | case FLabel.LEFT_CENTER:
|
577 | 4256 | caballero | float height = metrics.getMaxAscent();
|
578 | 4226 | caballero | g2.drawString(s, 0, 0 - (height / 2));
|
579 | |||
580 | 4213 | caballero | break;
|
581 | 4226 | caballero | |
582 | 4213 | caballero | case FLabel.LEFT_TOP:
|
583 | 4256 | caballero | height = metrics.getMaxAscent();
|
584 | 4226 | caballero | g2.drawString(s, 0, 0 - height);
|
585 | |||
586 | 4213 | caballero | break;
|
587 | 4226 | caballero | |
588 | 4213 | caballero | case FLabel.CENTER_BOTTOM:
|
589 | 4256 | caballero | float width = metrics.stringWidth(s);
|
590 | 4226 | caballero | g2.drawString(s, 0 - (width / 2), 0 - 3);
|
591 | |||
592 | 4213 | caballero | break;
|
593 | 4226 | caballero | |
594 | 4213 | caballero | case FLabel.CENTER_CENTER:
|
595 | 4256 | caballero | height = metrics.getMaxAscent();
|
596 | width = metrics.stringWidth(s);
|
||
597 | 4226 | caballero | g2.drawString(s, 0 - (width / 2),
|
598 | 0 - (height / 2));
|
||
599 | |||
600 | 4213 | caballero | break;
|
601 | 4226 | caballero | |
602 | 4213 | caballero | case FLabel.CENTER_TOP:
|
603 | 4256 | caballero | width = metrics.stringWidth(s);
|
604 | height = metrics.getMaxAscent();
|
||
605 | 4226 | caballero | g2.drawString(s, 0 - (width / 2), 0 -
|
606 | height);
|
||
607 | |||
608 | 4213 | caballero | break;
|
609 | 4226 | caballero | |
610 | 4213 | caballero | case FLabel.RIGHT_BOTTOM:
|
611 | 4256 | caballero | width = metrics.stringWidth(s);
|
612 | 4226 | caballero | g2.drawString(s, 0 - width, 0 - 3);
|
613 | |||
614 | 4213 | caballero | break;
|
615 | 4226 | caballero | |
616 | 4213 | caballero | case FLabel.RIGHT_CENTER:
|
617 | 4256 | caballero | width = metrics.stringWidth(s);
|
618 | height = metrics.getMaxAscent();
|
||
619 | 4226 | caballero | g2.drawString(s, 0 - width, 0 -
|
620 | (height / 2));
|
||
621 | |||
622 | 4213 | caballero | break;
|
623 | 4226 | caballero | |
624 | 4213 | caballero | case FLabel.RIGHT_TOP:
|
625 | 4256 | caballero | width = metrics.stringWidth(s);
|
626 | height = metrics.getMaxAscent();
|
||
627 | 4226 | caballero | g2.drawString(s, 0 - width, 0 - height);
|
628 | |||
629 | 4213 | caballero | break;
|
630 | }
|
||
631 | 5511 | caballero | */
|
632 | 4256 | caballero | ///Rectangle2D borde=vp.fromMapRectangle(theLabel.getBoundBox());//theLabel.getBoundingBox();
|
633 | ///g2.setColor(Color.blue);
|
||
634 | ///g2.drawRect((int)borde.getX(),(int)borde.getY(),(int)borde.getWidth(),(int)borde.getHeight());
|
||
635 | 4213 | caballero | // Restauramos
|
636 | g2.setTransform(ant); |
||
637 | } |
||
638 | |||
639 | 1005 | vcaballero | /**
|
640 | * Dibuja un punto sobre el Graphics2D que se pasa como par?metro.
|
||
641 | *
|
||
642 | * @param g2 Graphics2D sobre el que dibujar.
|
||
643 | * @param mT MAtriz de transformaci?n.
|
||
644 | * @param pAux punto a dibujar.
|
||
645 | * @param theSymbol S?mbolo a aplicar.
|
||
646 | */
|
||
647 | private static void drawSymbolPoint(Graphics2D g2, AffineTransform mT, |
||
648 | FPoint2D pAux, FSymbol theSymbol) { |
||
649 | int x;
|
||
650 | int y;
|
||
651 | x = (int) pAux.getX();
|
||
652 | y = (int) pAux.getY();
|
||
653 | |||
654 | 452 | fjp | /*if (x==0){
|
655 | 1005 | vcaballero | x=100;
|
656 | }
|
||
657 | if (y==0){
|
||
658 | y=100;
|
||
659 | }
|
||
660 | */
|
||
661 | Rectangle rectAux = new Rectangle(); |
||
662 | |||
663 | // Aqu? hay que mirar m_Size y m_useSize...
|
||
664 | float radio_simbolo;
|
||
665 | radio_simbolo = theSymbol.getSize() / 2;
|
||
666 | 5259 | fjp | // theSymbol.setSizeInPixels(true);
|
667 | 1005 | vcaballero | |
668 | if (!theSymbol.isSizeInPixels()) {
|
||
669 | // Suponemos que m_Size viene en coordenadas de mundo real
|
||
670 | radio_simbolo = (float) (theSymbol.getSize() * mT.getScaleX());
|
||
671 | |||
672 | /* System.out.println("m_bUseSize = " + theSymbol.m_bUseSize +
|
||
673 | " Escala: " + mT.getScaleX() + " alturaPixels = " + alturaPixels); */
|
||
674 | // if (radio_simbolo < 1) return; // No dibujamos nada
|
||
675 | rectAux.setRect(x - radio_simbolo, y - radio_simbolo, |
||
676 | radio_simbolo * 2, radio_simbolo * 2); |
||
677 | } else {
|
||
678 | // m_Size viene en pixels
|
||
679 | rectAux.setRect(x - radio_simbolo, y - radio_simbolo, |
||
680 | theSymbol.getSize(), theSymbol.getSize()); |
||
681 | 452 | fjp | } |
682 | 1005 | vcaballero | |
683 | // continue; //radioSimbolo_en_pixels = 3;
|
||
684 | if (theSymbol.getFill() != null) { |
||
685 | g2.setPaint(theSymbol.getFill()); |
||
686 | 452 | fjp | } |
687 | |||
688 | 1005 | vcaballero | if (theSymbol.getStroke() != null) { |
689 | g2.setStroke(theSymbol.getStroke()); |
||
690 | } |
||
691 | 452 | fjp | |
692 | 1005 | vcaballero | if (radio_simbolo < 2) { |
693 | g2.fillRect(rectAux.x, rectAux.y, rectAux.width, rectAux.height); |
||
694 | 452 | fjp | |
695 | 1005 | vcaballero | return;
|
696 | } |
||
697 | 452 | fjp | |
698 | 1005 | vcaballero | switch (theSymbol.getStyle()) {
|
699 | case FConstant.SYMBOL_STYLE_MARKER_CIRCLE: // Circulito |
||
700 | 452 | fjp | |
701 | 1005 | vcaballero | if (theSymbol.getColor() != null) { |
702 | g2.fillOval(rectAux.x, rectAux.y, rectAux.width, |
||
703 | rectAux.height); |
||
704 | } |
||
705 | 452 | fjp | |
706 | 1005 | vcaballero | if (theSymbol.isOutlined()) {
|
707 | g2.setColor(theSymbol.getOutlineColor()); |
||
708 | g2.drawOval(rectAux.x, rectAux.y, rectAux.width, |
||
709 | rectAux.height); |
||
710 | } |
||
711 | 452 | fjp | |
712 | 1005 | vcaballero | break;
|
713 | 452 | fjp | |
714 | 1005 | vcaballero | case FConstant.SYMBOL_STYLE_MARKER_SQUARE: // Cuadrado |
715 | 5259 | fjp | case FConstant.SYMBOL_STYLE_FILL_SOLID:
|
716 | 1005 | vcaballero | g2.fillRect(rectAux.x, rectAux.y, rectAux.width, rectAux.height); |
717 | 11547 | caballero | |
718 | 1005 | vcaballero | if (theSymbol.isOutlined()) {
|
719 | g2.setColor(theSymbol.getOutlineColor()); |
||
720 | g2.drawRect(rectAux.x, rectAux.y, rectAux.width, |
||
721 | rectAux.height); |
||
722 | } |
||
723 | 452 | fjp | |
724 | 1005 | vcaballero | break;
|
725 | 452 | fjp | |
726 | 1005 | vcaballero | case FConstant.SYMBOL_STYLE_MARKER_TRIANGLE: // Triangulo |
727 | 452 | fjp | |
728 | 1005 | vcaballero | // y = r*sin30, x = r*cos30
|
729 | GeneralPathX genPath = new GeneralPathX();
|
||
730 | genPath.moveTo(x - (int) (radio_simbolo * 0.866), |
||
731 | y + (int) (radio_simbolo * 0.5)); |
||
732 | genPath.lineTo(x + (int) (radio_simbolo * 0.866), |
||
733 | y + (int) (radio_simbolo * 0.5)); |
||
734 | genPath.lineTo(x, y - (float) radio_simbolo);
|
||
735 | genPath.closePath(); |
||
736 | 452 | fjp | |
737 | 1005 | vcaballero | g2.fill(genPath); |
738 | 452 | fjp | |
739 | 1005 | vcaballero | break;
|
740 | 452 | fjp | |
741 | 1402 | fjp | case FConstant.SYMBOL_STYLE_MARKER_CROSS: // cruz |
742 | case FConstant.SYMBOL_STYLE_DGNSPECIAL: // Cruz |
||
743 | 452 | fjp | |
744 | 1005 | vcaballero | GeneralPathX genPathCruz = new GeneralPathX();
|
745 | genPathCruz.moveTo(x, y - radio_simbolo); |
||
746 | genPathCruz.lineTo(x, y + radio_simbolo); |
||
747 | genPathCruz.moveTo(x - radio_simbolo, y); |
||
748 | genPathCruz.lineTo(x + radio_simbolo, y); |
||
749 | g2.draw(genPathCruz); |
||
750 | 452 | fjp | |
751 | 1005 | vcaballero | break;
|
752 | 452 | fjp | |
753 | 1005 | vcaballero | case 34: // TrueType marker |
754 | 452 | fjp | |
755 | 1005 | vcaballero | /* lf.lfHeight = -radioSimbolo_en_pixels;
|
756 | angulo = pSimbolo->m_Rotation; // En radianes, de -pi a pi
|
||
757 | angulo = -180.0 * angulo / PI;
|
||
758 | 3748 | caballero | |
759 | 1005 | vcaballero | lf.lfEscapement = (long) angulo*10;
|
760 | lf.lfOrientation = (long) angulo*10;
|
||
761 | 3748 | caballero | |
762 | 1005 | vcaballero | fuente.CreateFontIndirect(&lf);
|
763 | pOldFont = pDC->SelectObject(&fuente);
|
||
764 | 3748 | caballero | |
765 | 1005 | vcaballero | pDC->TextOut(pAPI.x, pAPI.y+radioSimbolo_en_pixels/2,elChar,1);
|
766 | 3748 | caballero | |
767 | 1005 | vcaballero | pDC->SelectObject(pOldFont);
|
768 | fuente.DeleteObject();
|
||
769 | 3748 | caballero | |
770 | 1005 | vcaballero | break; */
|
771 | case FConstant.SYMBOL_STYLE_MARKER_IMAGEN: // Icono |
||
772 | { |
||
773 | if (theSymbol.getIcon() != null) { |
||
774 | float w;
|
||
775 | float h;
|
||
776 | 452 | fjp | |
777 | 1836 | fernando | if (!theSymbol.isSizeInPixels()) {
|
778 | 1005 | vcaballero | // Suponemos que m_Size viene en coordenadas de mundo real
|
779 | // Por ejemplo, nos valemos del ancho para fijar la escala
|
||
780 | w = (float) (theSymbol.getSize() * mT.getScaleX());
|
||
781 | h = (theSymbol.getIcon().getHeight(null) * w) / theSymbol.getIcon()
|
||
782 | .getWidth(null);
|
||
783 | 452 | fjp | |
784 | 1005 | vcaballero | rectAux.setRect(x - w, y - h, w * 2, h * 2); |
785 | } else {
|
||
786 | // m_Size viene en pixels
|
||
787 | w = theSymbol.getSize(); |
||
788 | h = (theSymbol.getIcon().getHeight(null) * w) / theSymbol.getIcon()
|
||
789 | .getWidth(null);
|
||
790 | 3589 | caballero | rectAux.setRect(x - w/2, y - h/2, w, h); |
791 | 1005 | vcaballero | } |
792 | 4757 | fjp | /* if (theSymbol.getImgObserver() != null)
|
793 | {
|
||
794 | g2.setColor(Color.WHITE);
|
||
795 | g2.fillRect(rectAux.x, rectAux.y, rectAux.width, rectAux.height);
|
||
796 | } */
|
||
797 | 1005 | vcaballero | g2.drawImage(theSymbol.getIcon(), rectAux.x, rectAux.y, |
798 | 4757 | fjp | rectAux.width, rectAux.height, theSymbol.getImgObserver()); |
799 | 1005 | vcaballero | } else {
|
800 | 3748 | caballero | String strImg = "Image"; // Utilities.getMessage(FGraphicUtilities.class,"imagen"); |
801 | 1005 | vcaballero | FontMetrics metrics = g2.getFontMetrics();
|
802 | int width = metrics.stringWidth(strImg);
|
||
803 | int height = metrics.getMaxAscent();
|
||
804 | 452 | fjp | |
805 | 1005 | vcaballero | g2.drawString(strImg, x - (width / 2), y - 2 + |
806 | (height / 2));
|
||
807 | } |
||
808 | 452 | fjp | |
809 | 1005 | vcaballero | break;
|
810 | } |
||
811 | 452 | fjp | |
812 | 1005 | vcaballero | /* DrawIconEx(pDC->m_hDC, pAPI.x-(pSimbolo->m_widthIco/2), pAPI.y-(pSimbolo->m_heightIco/2),
|
813 | pSimbolo->m_hIcon, pSimbolo->m_widthIco, pSimbolo->m_heightIco, 0 , NULL, DI_NORMAL);
|
||
814 | break; */
|
||
815 | case FConstant.SYMBOL_STYLE_POINTZ: // Circulito |
||
816 | 452 | fjp | |
817 | 1005 | vcaballero | if (theSymbol.getColor() != null) { |
818 | g2.fillOval(rectAux.x, rectAux.y, rectAux.width, |
||
819 | rectAux.height); |
||
820 | } |
||
821 | 452 | fjp | |
822 | 1005 | vcaballero | if (theSymbol.isOutlined()) {
|
823 | g2.setColor(theSymbol.getOutlineColor()); |
||
824 | g2.drawOval(rectAux.x, rectAux.y, rectAux.width, |
||
825 | rectAux.height); |
||
826 | } |
||
827 | 452 | fjp | |
828 | 1005 | vcaballero | break;
|
829 | } // del switch estilo
|
||
830 | } |
||
831 | 3833 | caballero | public static void DrawHandlers(Graphics2D g, AffineTransform at, |
832 | 11434 | caballero | Handler[] handlers, FSymbol symbol) { |
833 | 3748 | caballero | |
834 | 3833 | caballero | for (int i = 0; i < handlers.length; i++) { |
835 | Point2D point = handlers[i].getPoint();
|
||
836 | at.transform(point, point); |
||
837 | 11434 | caballero | g.setColor(symbol.getColor()); |
838 | 3886 | caballero | g.fillRect((int) (point.getX() - 3), (int) (point.getY() - 3), 7, 7); |
839 | 11434 | caballero | g.setColor(symbol.getOutlineColor()); |
840 | 3833 | caballero | g.drawRect((int) (point.getX() - 5), (int) (point.getY() - 5), 10, 10); |
841 | 6013 | fjp | g.drawString( "" + i, (int) (point.getX() - 5), (int) (point.getY() - 5)); |
842 | 3833 | caballero | } |
843 | } |
||
844 | 4118 | caballero | |
845 | public static void DrawVertex(Graphics2D g, AffineTransform at, Handler handlers) { |
||
846 | //for (int i = 0; i < handlers.length; i++) {
|
||
847 | Point2D point = handlers.getPoint();
|
||
848 | at.transform(point, point); |
||
849 | g.setColor(Color.red);
|
||
850 | g.drawLine((int)point.getX()-1,(int)point.getY()-10,(int)point.getX()-1,(int)point.getY()+10); |
||
851 | g.drawLine((int)point.getX()+1,(int)point.getY()-10,(int)point.getX()+1,(int)point.getY()+10); |
||
852 | g.drawLine((int)point.getX()-10,(int)point.getY()-1,(int)point.getX()+10,(int)point.getY()-1); |
||
853 | g.drawLine((int)point.getX()-10,(int)point.getY()+1,(int)point.getX()+10,(int)point.getY()+1); |
||
854 | //}
|
||
855 | } |
||
856 | 452 | fjp | } |