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

Last change on this file since 18544 was 18544, checked in by pieren, 16 years ago

Add licence in headers for GPL compliance.

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