source: osm/applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/WMSLayer.java@ 20211

Last change on this file since 20211 was 20211, checked in by pieren, 14 years ago

Fixed image rotation and a null pointer exception in adjust mode (raster maps only).

  • Property svn:eol-style set to native
File size: 24.2 KB
Line 
1// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3@gmail.com> and others
2package cadastre_fr;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.awt.Component;
8import java.awt.Graphics;
9import java.awt.Graphics2D;
10import java.awt.Image;
11import java.awt.Point;
12import java.awt.RenderingHints;
13import java.awt.Toolkit;
14import java.awt.image.BufferedImage;
15import java.awt.image.ImageObserver;
16import java.io.EOFException;
17import java.io.IOException;
18import java.io.ObjectInputStream;
19import java.io.ObjectOutputStream;
20import java.util.ArrayList;
21import java.util.Vector;
22
23import javax.swing.Icon;
24import javax.swing.ImageIcon;
25import javax.swing.JMenuItem;
26import javax.swing.JOptionPane;
27
28import org.openstreetmap.josm.Main;
29import org.openstreetmap.josm.data.Bounds;
30import org.openstreetmap.josm.data.coor.EastNorth;
31import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
32import org.openstreetmap.josm.gui.MapView;
33import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
34import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
35import org.openstreetmap.josm.gui.layer.Layer;
36import org.openstreetmap.josm.io.OsmTransferException;
37
38/**
39 * This is a layer that grabs the current screen from the French cadastre WMS
40 * server. The data fetched this way is tiled and managed to the disc to reduce
41 * server load.
42 */
43public class WMSLayer extends Layer implements ImageObserver {
44
45 Component[] component = null;
46
47 private int lambertZone = -1;
48
49 protected static final Icon icon = new ImageIcon(Toolkit.getDefaultToolkit().createImage(
50 CadastrePlugin.class.getResource("/images/cadastre_small.png")));
51
52 protected Vector<GeorefImage> images = new Vector<GeorefImage>();
53
54 /**
55 * v1 to v2 = not supported
56 * v2 to v3 = add 4 more EastNorth coordinates in GeorefImages
57 * v3 to v4 = add original raster image width and height
58 */
59 protected final int serializeFormatVersion = 4;
60
61 public static int currentFormat;
62
63 private ArrayList<EastNorthBound> dividedBbox = new ArrayList<EastNorthBound>();
64
65 private CacheControl cacheControl = null;
66
67 private String location = "";
68
69 private String codeCommune = "";
70
71 public EastNorthBound communeBBox = new EastNorthBound(new EastNorth(0,0), new EastNorth(0,0));
72
73 private boolean isRaster = false;
74
75 private boolean isAlreadyGeoreferenced = false;
76
77 public double X0, Y0, angle, fX, fY;
78
79 // bbox of the georeferenced raster image (the nice horizontal and vertical box)
80 private EastNorth rasterMin;
81 private EastNorth rasterMax;
82 private double rasterRatio;
83
84 private JMenuItem saveAsPng;
85
86 public boolean adjustModeEnabled;
87
88 public WMSLayer() {
89 this(tr("Blank Layer"), "", -1);
90 }
91
92 public WMSLayer(String location, String codeCommune, int lambertZone) {
93 super(buildName(location, codeCommune));
94 this.location = location;
95 this.codeCommune = codeCommune;
96 this.lambertZone = lambertZone;
97 // enable auto-sourcing option
98 CadastrePlugin.pluginUsed = true;
99 }
100
101 public void destroy() {
102 // if the layer is currently saving the images in the cache, wait until it's finished
103 if (cacheControl != null) {
104 while (!cacheControl.isCachePipeEmpty()) {
105 System.out.println("Try to close a WMSLayer which is currently saving in cache : wait 1 sec.");
106 CadastrePlugin.safeSleep(1000);
107 }
108 }
109 super.destroy();
110 images = null;
111 dividedBbox = null;
112 System.out.println("Layer "+location+" destroyed");
113 }
114
115 private static String buildName(String location, String codeCommune) {
116 String ret = new String(location.toUpperCase());
117 if (codeCommune != null && !codeCommune.equals(""))
118 ret += "(" + codeCommune + ")";
119 return ret;
120 }
121
122 private String rebuildName() {
123 return buildName(this.location.toUpperCase(), this.codeCommune);
124 }
125
126 public void grab(CadastreGrabber grabber, Bounds b) throws IOException {
127 grab(grabber, b, true);
128 }
129
130 public void grab(CadastreGrabber grabber, Bounds b, boolean useFactor) throws IOException {
131 if (useFactor) {
132 if (isRaster) {
133 b = new Bounds(Main.proj.eastNorth2latlon(rasterMin), Main.proj.eastNorth2latlon(rasterMax));
134 divideBbox(b, Integer.parseInt(Main.pref.get("cadastrewms.rasterDivider",
135 CadastrePreferenceSetting.DEFAULT_RASTER_DIVIDER)));
136 } else
137 divideBbox(b, Integer.parseInt(Main.pref.get("cadastrewms.scale", Scale.X1.toString())));
138 } else
139 divideBbox(b, 1);
140
141 for (EastNorthBound n : dividedBbox) {
142 GeorefImage newImage;
143 try {
144 newImage = grabber.grab(this, n.min, n.max);
145 } catch (IOException e) {
146 System.out.println("Download action cancelled by user or server did not respond");
147 break;
148 } catch (OsmTransferException e) {
149 System.out.println("OSM transfer failed");
150 break;
151 }
152 if (grabber.getWmsInterface().downloadCancelled) {
153 System.out.println("Download action cancelled by user");
154 break;
155 }
156 if (CadastrePlugin.backgroundTransparent) {
157 for (GeorefImage img : images) {
158 if (img.overlap(newImage))
159 // mask overlapping zone in already grabbed image
160 img.withdraw(newImage);
161 else
162 // mask overlapping zone in new image only when new
163 // image covers completely the existing image
164 newImage.withdraw(img);
165 }
166 }
167 images.add(newImage);
168 saveToCache(newImage);
169 Main.map.mapView.repaint();
170 }
171 }
172
173 /**
174 *
175 * @param b the original bbox, usually the current bbox on screen
176 * @param factor 1 = source bbox 1:1
177 * 2 = source bbox divided by 2x2 smaller boxes
178 * 3 = source bbox divided by 3x3 smaller boxes
179 * 4 = hard coded size of boxes (100 meters) rounded allowing
180 * grabbing of next contiguous zone
181 */
182 private void divideBbox(Bounds b, int factor) {
183 EastNorth lambertMin = Main.proj.latlon2eastNorth(b.getMin());
184 EastNorth lambertMax = Main.proj.latlon2eastNorth(b.getMax());
185 double minEast = lambertMin.east();
186 double minNorth = lambertMin.north();
187 double dEast = (lambertMax.east() - minEast) / factor;
188 double dNorth = (lambertMax.north() - minNorth) / factor;
189 dividedBbox.clear();
190 if (factor < 4 || isRaster) {
191 for (int xEast = 0; xEast < factor; xEast++)
192 for (int xNorth = 0; xNorth < factor; xNorth++) {
193 dividedBbox.add(new EastNorthBound(new EastNorth(minEast + xEast * dEast, minNorth + xNorth * dNorth),
194 new EastNorth(minEast + (xEast + 1) * dEast, minNorth + (xNorth + 1) * dNorth)));
195 }
196 } else {
197 // divide to fixed size squares
198 int cSquare = Integer.parseInt(Main.pref.get("cadastrewms.squareSize", "100"));
199 minEast = minEast - minEast % cSquare;
200 minNorth = minNorth - minNorth % cSquare;
201 for (int xEast = (int)minEast; xEast < lambertMax.east(); xEast+=cSquare)
202 for (int xNorth = (int)minNorth; xNorth < lambertMax.north(); xNorth+=cSquare) {
203 dividedBbox.add(new EastNorthBound(new EastNorth(xEast, xNorth),
204 new EastNorth(xEast + cSquare, xNorth + cSquare)));
205 }
206 }
207 }
208
209 @Override
210 public Icon getIcon() {
211 return icon;
212 }
213
214 @Override
215 public String getToolTipText() {
216 String str = tr("WMS layer ({0}), {1} tile(s) loaded", getName(), images.size());
217 if (isRaster) {
218 str += "\n"+tr("Is not vectorized.");
219 str += "\n"+tr("Raster size: {0}", communeBBox);
220 } else
221 str += "\n"+tr("Is vectorized.");
222 str += "\n"+tr("Commune bbox: {0}", communeBBox);
223 return str;
224 }
225
226 @Override
227 public boolean isMergable(Layer other) {
228 return false;
229 }
230
231 @Override
232 public void mergeFrom(Layer from) {
233 }
234
235 @Override
236 public void paint(Graphics2D g, final MapView mv, Bounds bounds) {
237 synchronized(this){
238 Object savedInterpolation = g.getRenderingHint(RenderingHints.KEY_INTERPOLATION);
239 if (savedInterpolation == null) savedInterpolation = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
240 String interpolation = Main.pref.get("cadastrewms.imageInterpolation", "Standard");
241 if (interpolation.equals("bilinear"))
242 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
243 else if (interpolation.equals("bicubic"))
244 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
245 else
246 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
247 for (GeorefImage img : images)
248 img.paint(g, mv, CadastrePlugin.backgroundTransparent,
249 CadastrePlugin.transparency, CadastrePlugin.drawBoundaries);
250 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, savedInterpolation);
251 }
252 if (this.isRaster) {
253 paintCrosspieces(g, mv);
254 }
255 if (this.adjustModeEnabled) {
256 WMSAdjustAction.paintAdjustFrames(g, mv);
257 }
258 }
259
260 @Override
261 public void visitBoundingBox(BoundingXYVisitor v) {
262 for (GeorefImage img : images) {
263 v.visit(img.min);
264 v.visit(img.max);
265 }
266 }
267
268 @Override
269 public Object getInfoComponent() {
270 return getToolTipText();
271 }
272
273 @Override
274 public Component[] getMenuEntries() {
275 saveAsPng = new JMenuItem(new MenuActionSaveRasterAs(this));
276 saveAsPng.setEnabled(isRaster);
277 component = new Component[] { new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
278 new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
279 new JMenuItem(new MenuActionLoadFromCache()),
280 saveAsPng,
281 new JMenuItem(new LayerListPopup.InfoAction(this)),
282
283 };
284 return component;
285 }
286
287 public GeorefImage findImage(EastNorth eastNorth) {
288 // Iterate in reverse, so we return the image which is painted last.
289 // (i.e. the topmost one)
290 for (int i = images.size() - 1; i >= 0; i--) {
291 if (images.get(i).contains(eastNorth)) {
292 return images.get(i);
293 }
294 }
295 return null;
296 }
297
298 public boolean isOverlapping(Bounds bounds) {
299 GeorefImage georefImage =
300 new GeorefImage(new BufferedImage(1,1,BufferedImage.TYPE_INT_RGB ), // not really important
301 Main.proj.latlon2eastNorth(bounds.getMin()),
302 Main.proj.latlon2eastNorth(bounds.getMax()));
303 for (GeorefImage img : images) {
304 if (img.overlap(georefImage))
305 return true;
306 }
307 return false;
308 }
309
310 public void saveToCache(GeorefImage image) {
311 if (CacheControl.cacheEnabled && !isRaster()) {
312 getCacheControl().saveCache(image);
313 }
314 }
315
316 public void saveNewCache() {
317 if (CacheControl.cacheEnabled) {
318 getCacheControl().deleteCacheFile();
319 for (GeorefImage image : images)
320 getCacheControl().saveCache(image);
321 }
322 }
323
324 public CacheControl getCacheControl() {
325 if (cacheControl == null)
326 cacheControl = new CacheControl(this);
327 return cacheControl;
328 }
329
330 /**
331 * Convert the eastNorth input coordinates to raster coordinates.
332 * The original raster size is [0,0,12286,8730] where 0,0 is the upper left corner and
333 * 12286,8730 is the approx. raster max size.
334 * @return the raster coordinates for the wms server request URL (minX,minY,maxX,maxY)
335 */
336 public String eastNorth2raster(EastNorth min, EastNorth max) {
337 double minX = (min.east() - rasterMin.east()) / rasterRatio;
338 double minY = (min.north() - rasterMin.north()) / rasterRatio;
339 double maxX = (max.east() - rasterMin.east()) / rasterRatio;
340 double maxY = (max.north() - rasterMin.north()) / rasterRatio;
341 return minX+","+minY+","+maxX+","+maxY;
342 }
343
344
345 public String getLocation() {
346 return location;
347 }
348
349 public void setLocation(String location) {
350 this.location = location;
351 setName(rebuildName());
352 }
353
354 public String getCodeCommune() {
355 return codeCommune;
356 }
357
358 public void setCodeCommune(String codeCommune) {
359 this.codeCommune = codeCommune;
360 setName(rebuildName());
361 }
362
363 public boolean isRaster() {
364 return isRaster;
365 }
366
367 public void setRaster(boolean isRaster) {
368 this.isRaster = isRaster;
369 if (saveAsPng != null)
370 saveAsPng.setEnabled(isRaster);
371 }
372
373 public boolean isAlreadyGeoreferenced() {
374 return isAlreadyGeoreferenced;
375 }
376
377 public void setAlreadyGeoreferenced(boolean isAlreadyGeoreferenced) {
378 this.isAlreadyGeoreferenced = isAlreadyGeoreferenced;
379 }
380
381 /**
382 * Set raster positions used for grabbing and georeferencing.
383 * rasterMin is the Eaast North of bottom left corner raster image on the screen when image is grabbed.
384 * The bounds width and height are the raster width and height. The image width matches the current view
385 * and the image height is adapted.
386 * Required: the communeBBox must be set (normally it is catched by CadastreInterface and saved by DownloadWMSPlanImage)
387 * @param bounds the current main map view boundaries
388 */
389 public void setRasterBounds(Bounds bounds) {
390 EastNorth rasterCenter = Main.proj.latlon2eastNorth(bounds.getCenter());
391 EastNorth eaMin = Main.proj.latlon2eastNorth(bounds.getMin());
392 EastNorth eaMax = Main.proj.latlon2eastNorth(bounds.getMax());
393 double rasterSizeX = communeBBox.max.getX() - communeBBox.min.getX();
394 double rasterSizeY = communeBBox.max.getY() - communeBBox.min.getY();
395 double ratio = rasterSizeY/rasterSizeX;
396 // keep same ratio on screen as WMS bbox (stored in communeBBox)
397 rasterMin = new EastNorth(eaMin.getX(), rasterCenter.getY()-(eaMax.getX()-eaMin.getX())*ratio/2);
398 rasterMax = new EastNorth(eaMax.getX(), rasterCenter.getY()+(eaMax.getX()-eaMin.getX())*ratio/2);
399 rasterRatio = (rasterMax.getX()-rasterMin.getX())/rasterSizeX;
400 }
401
402 /**
403 * Called by CacheControl when a new cache file is created on disk.
404 * Save only primitives to keep cache independent of software changes.
405 * @param oos
406 * @throws IOException
407 */
408 public void write(ObjectOutputStream oos) throws IOException {
409 oos.writeInt(this.serializeFormatVersion);
410 oos.writeObject(this.location); // String
411 oos.writeObject(this.codeCommune); // String
412 oos.writeInt(this.lambertZone);
413 oos.writeBoolean(this.isRaster);
414 if (this.isRaster) {
415 oos.writeDouble(this.rasterMin.getX());
416 oos.writeDouble(this.rasterMin.getY());
417 oos.writeDouble(this.rasterMax.getX());
418 oos.writeDouble(this.rasterMax.getY());
419 oos.writeDouble(this.rasterRatio);
420 }
421 oos.writeDouble(this.communeBBox.min.getX());
422 oos.writeDouble(this.communeBBox.min.getY());
423 oos.writeDouble(this.communeBBox.max.getX());
424 oos.writeDouble(this.communeBBox.max.getY());
425 }
426
427 /**
428 * Called by CacheControl when a cache file is read from disk.
429 * Cache uses only primitives to stay independent of software changes.
430 * @param ois
431 * @throws IOException
432 * @throws ClassNotFoundException
433 */
434 public boolean read(ObjectInputStream ois, int currentLambertZone) throws IOException, ClassNotFoundException {
435 currentFormat = ois.readInt();;
436 if (currentFormat < 2) {
437 JOptionPane.showMessageDialog(Main.parent, tr("Unsupported cache file version; found {0}, expected {1}\nCreate a new one.",
438 currentFormat, this.serializeFormatVersion), tr("Cache Format Error"), JOptionPane.ERROR_MESSAGE);
439 return false;
440 }
441 this.setLocation((String) ois.readObject());
442 this.setCodeCommune((String) ois.readObject());
443 this.lambertZone = ois.readInt();
444 this.setRaster(ois.readBoolean());
445 if (this.isRaster) {
446 double X = ois.readDouble();
447 double Y = ois.readDouble();
448 this.rasterMin = new EastNorth(X, Y);
449 X = ois.readDouble();
450 Y = ois.readDouble();
451 this.rasterMax = new EastNorth(X, Y);
452 this.rasterRatio = ois.readDouble();
453 }
454 double minX = ois.readDouble();
455 double minY = ois.readDouble();
456 double maxX = ois.readDouble();
457 double maxY = ois.readDouble();
458 this.communeBBox = new EastNorthBound(new EastNorth(minX, minY), new EastNorth(maxX, maxY));
459 if (this.lambertZone != currentLambertZone && currentLambertZone != -1) {
460 JOptionPane.showMessageDialog(Main.parent, tr("Lambert zone {0} in cache "+
461 "incompatible with current Lambert zone {1}",
462 this.lambertZone+1, currentLambertZone), tr("Cache Lambert Zone Error"), JOptionPane.ERROR_MESSAGE);
463 return false;
464 }
465 synchronized(this){
466 boolean EOF = false;
467 try {
468 while (!EOF) {
469 GeorefImage newImage = (GeorefImage) ois.readObject();
470 for (GeorefImage img : this.images) {
471 if (CadastrePlugin.backgroundTransparent) {
472 if (img.overlap(newImage))
473 // mask overlapping zone in already grabbed image
474 img.withdraw(newImage);
475 else
476 // mask overlapping zone in new image only when
477 // new image covers completely the existing image
478 newImage.withdraw(img);
479 }
480 }
481 this.images.add(newImage);
482 }
483 } catch (EOFException ex) {
484 // expected exception when all images are read
485 }
486 }
487 System.out.println("Cache loaded for location "+location+" with "+images.size()+" images");
488 return true;
489 }
490
491 /**
492 * Join the grabbed images into one single.
493 * Works only for images grabbed from non-georeferenced images (Feuilles cadastrales)(same amount of
494 * images in x and y)
495 */
496 public void joinRasterImages() {
497 if (images.size() > 1) {
498 EastNorth min = images.get(0).min;
499 EastNorth max = images.get(images.size()-1).max;
500 int oldImgWidth = images.get(0).image.getWidth();
501 int oldImgHeight = images.get(0).image.getHeight();
502 int newWidth = oldImgWidth*(int)Math.sqrt(images.size());
503 int newHeight = oldImgHeight*(int)Math.sqrt(images.size());
504 BufferedImage new_img = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
505 Graphics g = new_img.getGraphics();
506 // Coordinate (0,0) is on top,left corner where images are grabbed from bottom left
507 int rasterDivider = (int)Math.sqrt(images.size());
508 for (int h = 0; h < rasterDivider; h++) {
509 for (int v = 0; v < rasterDivider; v++) {
510 int newx = h*oldImgWidth;
511 int newy = newHeight - oldImgHeight - (v*oldImgHeight);
512 int j = h*rasterDivider + v;
513 g.drawImage(images.get(j).image, newx, newy, this);
514 }
515 }
516 synchronized(this) {
517 images.clear();
518 images.add(new GeorefImage(new_img, min, max));
519 }
520 }
521 }
522
523 /**
524 * Image cropping based on two EN coordinates pointing to two corners in diagonal
525 * Because it's coming from user mouse clics, we have to sort de positions first.
526 * Works only for raster image layer (only one image in collection).
527 * Updates layer georeferences.
528 * @param en1
529 * @param en2
530 */
531 public void cropImage(EastNorth en1, EastNorth en2){
532 // adj1 is corner bottom, left
533 EastNorth adj1 = new EastNorth(en1.east() <= en2.east() ? en1.east() : en2.east(),
534 en1.north() <= en2.north() ? en1.north() : en2.north());
535 // adj2 is corner top, right
536 EastNorth adj2 = new EastNorth(en1.east() > en2.east() ? en1.east() : en2.east(),
537 en1.north() > en2.north() ? en1.north() : en2.north());
538 images.get(0).crop(adj1, adj2);
539 // update the layer georefs
540 rasterMin = adj1;
541 rasterMax = adj2;
542 setCommuneBBox(new EastNorthBound(new EastNorth(0,0), new EastNorth(images.get(0).image.getWidth()-1,images.get(0).image.getHeight()-1)));
543 rasterRatio = (rasterMax.getX()-rasterMin.getX())/(communeBBox.max.getX() - communeBBox.min.getX());
544 }
545
546 public EastNorthBound getCommuneBBox() {
547 return communeBBox;
548 }
549
550 public void setCommuneBBox(EastNorthBound entireCommune) {
551 this.communeBBox = entireCommune;
552 }
553
554 /**
555 * Method required by ImageObserver when drawing an image
556 */
557 public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
558 return false;
559 }
560
561 public int getLambertZone() {
562 return lambertZone;
563 }
564
565 public EastNorth getRasterCenter() {
566 return new EastNorth((images.get(0).max.east()+images.get(0).min.east())/2,
567 (images.get(0).max.north()+images.get(0).min.north())/2);
568 }
569
570 public void displace(double dx, double dy) {
571 this.rasterMin = new EastNorth(rasterMin.east() + dx, rasterMin.north() + dy);
572 this.rasterMax = new EastNorth(rasterMax.east() + dx, rasterMax.north() + dy);
573 images.get(0).shear(dx, dy);
574 }
575
576 public void resize(EastNorth rasterCenter, double proportion) {
577 this.rasterMin = rasterMin.interpolate(rasterCenter, proportion);
578 this.rasterMax = rasterMax.interpolate(rasterCenter, proportion);
579 images.get(0).scale(rasterCenter, proportion);
580 }
581
582 public void rotate(EastNorth rasterCenter, double angle) {
583 this.rasterMin = rasterMin.rotate(rasterCenter, angle);
584 this.rasterMax = rasterMax.rotate(rasterCenter, angle);
585// double proportion = dst1.distance(dst2)/org1.distance(org2);
586 images.get(0).rotate(rasterCenter, angle);
587 this.angle += angle;
588 }
589
590 private void paintCrosspieces(Graphics g, MapView mv) {
591 String crosspieces = Main.pref.get("cadastrewms.crosspieces", "0");
592 if (!crosspieces.equals("0")) {
593 int modulo = 25;
594 if (crosspieces.equals("2")) modulo = 50;
595 if (crosspieces.equals("3")) modulo = 100;
596 EastNorthBound currentView = new EastNorthBound(mv.getEastNorth(0, mv.getHeight()),
597 mv.getEastNorth(mv.getWidth(), 0));
598 int minX = ((int)currentView.min.east()/modulo+1)*modulo;
599 int minY = ((int)currentView.min.north()/modulo+1)*modulo;
600 int maxX = ((int)currentView.max.east()/modulo)*modulo;
601 int maxY = ((int)currentView.max.north()/modulo)*modulo;
602 int size=(maxX-minX)/modulo;
603 if (size<20) {
604 int px= size > 10 ? 2 : Math.abs(12-size);
605 g.setColor(Color.green);
606 for (int x=minX; x<=maxX; x+=modulo) {
607 for (int y=minY; y<=maxY; y+=modulo) {
608 Point p = mv.getPoint(new EastNorth(x,y));
609 g.drawLine(p.x-px, p.y, p.x+px, p.y);
610 g.drawLine(p.x, p.y-px, p.x, p.y+px);
611 }
612 }
613 }
614 }
615 }
616
617}
Note: See TracBrowser for help on using the repository browser.