source: josm/trunk/src/org/openstreetmap/josm/gui/MapViewState.java@ 13281

Last change on this file since 13281 was 13181, checked in by Don-vip, 6 years ago

fix #15599 - Improvements for testing painting (last patches by ris)

  • Property svn:eol-style set to native
File size: 25.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui;
3
4import java.awt.Container;
5import java.awt.GraphicsEnvironment;
6import java.awt.Point;
7import java.awt.geom.AffineTransform;
8import java.awt.geom.Area;
9import java.awt.geom.Path2D;
10import java.awt.geom.Point2D;
11import java.awt.geom.Point2D.Double;
12import java.awt.geom.Rectangle2D;
13import java.io.Serializable;
14import java.util.Objects;
15import java.util.Optional;
16
17import javax.swing.JComponent;
18
19import org.openstreetmap.josm.Main;
20import org.openstreetmap.josm.data.Bounds;
21import org.openstreetmap.josm.data.ProjectionBounds;
22import org.openstreetmap.josm.data.coor.EastNorth;
23import org.openstreetmap.josm.data.coor.ILatLon;
24import org.openstreetmap.josm.data.coor.LatLon;
25import org.openstreetmap.josm.data.osm.Node;
26import org.openstreetmap.josm.data.projection.Projecting;
27import org.openstreetmap.josm.data.projection.Projection;
28import org.openstreetmap.josm.gui.download.DownloadDialog;
29import org.openstreetmap.josm.tools.CheckParameterUtil;
30import org.openstreetmap.josm.tools.Geometry;
31import org.openstreetmap.josm.tools.JosmRuntimeException;
32import org.openstreetmap.josm.tools.bugreport.BugReport;
33
34/**
35 * This class represents a state of the {@link MapView}.
36 * @author Michael Zangl
37 * @since 10343
38 */
39public final class MapViewState implements Serializable {
40
41 private static final long serialVersionUID = 1L;
42
43 /**
44 * A flag indicating that the point is outside to the top of the map view.
45 * @since 10827
46 */
47 public static final int OUTSIDE_TOP = 1;
48
49 /**
50 * A flag indicating that the point is outside to the bottom of the map view.
51 * @since 10827
52 */
53 public static final int OUTSIDE_BOTTOM = 2;
54
55 /**
56 * A flag indicating that the point is outside to the left of the map view.
57 * @since 10827
58 */
59 public static final int OUTSIDE_LEFT = 4;
60
61 /**
62 * A flag indicating that the point is outside to the right of the map view.
63 * @since 10827
64 */
65 public static final int OUTSIDE_RIGHT = 8;
66
67 /**
68 * Additional pixels outside the view for where to start clipping.
69 */
70 private static final int CLIP_BOUNDS = 50;
71
72 private final transient Projecting projecting;
73
74 private final int viewWidth;
75 private final int viewHeight;
76
77 private final double scale;
78
79 /**
80 * Top left {@link EastNorth} coordinate of the view.
81 */
82 private final EastNorth topLeft;
83
84 private final Point topLeftOnScreen;
85 private final Point topLeftInWindow;
86
87 /**
88 * Create a new {@link MapViewState}
89 * @param projection The projection to use.
90 * @param viewWidth The view width
91 * @param viewHeight The view height
92 * @param scale The scale to use
93 * @param topLeft The top left corner in east/north space.
94 * @param topLeftInWindow The top left point in window
95 * @param topLeftOnScreen The top left point on screen
96 */
97 private MapViewState(Projecting projection, int viewWidth, int viewHeight, double scale, EastNorth topLeft,
98 Point topLeftInWindow, Point topLeftOnScreen) {
99 CheckParameterUtil.ensureParameterNotNull(projection, "projection");
100 CheckParameterUtil.ensureParameterNotNull(topLeft, "topLeft");
101 CheckParameterUtil.ensureParameterNotNull(topLeftInWindow, "topLeftInWindow");
102 CheckParameterUtil.ensureParameterNotNull(topLeftOnScreen, "topLeftOnScreen");
103
104 this.projecting = projection;
105 this.scale = scale;
106 this.topLeft = topLeft;
107
108 this.viewWidth = viewWidth;
109 this.viewHeight = viewHeight;
110 this.topLeftInWindow = topLeftInWindow;
111 this.topLeftOnScreen = topLeftOnScreen;
112 }
113
114 private MapViewState(Projecting projection, int viewWidth, int viewHeight, double scale, EastNorth topLeft) {
115 this(projection, viewWidth, viewHeight, scale, topLeft, new Point(0, 0), new Point(0, 0));
116 }
117
118 private MapViewState(EastNorth topLeft, MapViewState mvs) {
119 this(mvs.projecting, mvs.viewWidth, mvs.viewHeight, mvs.scale, topLeft, mvs.topLeftInWindow, mvs.topLeftOnScreen);
120 }
121
122 private MapViewState(double scale, MapViewState mvs) {
123 this(mvs.projecting, mvs.viewWidth, mvs.viewHeight, scale, mvs.topLeft, mvs.topLeftInWindow, mvs.topLeftOnScreen);
124 }
125
126 private MapViewState(JComponent position, MapViewState mvs) {
127 this(mvs.projecting, position.getWidth(), position.getHeight(), mvs.scale, mvs.topLeft,
128 findTopLeftInWindow(position), findTopLeftOnScreen(position));
129 }
130
131 private MapViewState(Projecting projecting, MapViewState mvs) {
132 this(projecting, mvs.viewWidth, mvs.viewHeight, mvs.scale, mvs.topLeft, mvs.topLeftInWindow, mvs.topLeftOnScreen);
133 }
134
135 private static Point findTopLeftInWindow(JComponent position) {
136 Point result = new Point();
137 // better than using swing utils, since this allows us to use the method if no screen is present.
138 Container component = position;
139 while (component != null) {
140 result.x += component.getX();
141 result.y += component.getY();
142 component = component.getParent();
143 }
144 return result;
145 }
146
147 private static Point findTopLeftOnScreen(JComponent position) {
148 if (GraphicsEnvironment.isHeadless()) {
149 // in our imaginary universe the window is always (10, 10) from the top left of the screen
150 Point topLeftInWindow = findTopLeftInWindow(position);
151 return new Point(topLeftInWindow.x + 10, topLeftInWindow.y + 10);
152 } else {
153 try {
154 return position.getLocationOnScreen();
155 } catch (JosmRuntimeException | IllegalArgumentException | IllegalStateException e) {
156 throw BugReport.intercept(e).put("position", position).put("parent", position::getParent);
157 }
158 }
159 }
160
161 /**
162 * The scale in east/north units per pixel.
163 * @return The scale.
164 */
165 public double getScale() {
166 return scale;
167 }
168
169 /**
170 * Gets the MapViewPoint representation for a position in view coordinates.
171 * @param x The x coordinate inside the view.
172 * @param y The y coordinate inside the view.
173 * @return The MapViewPoint.
174 */
175 public MapViewPoint getForView(double x, double y) {
176 return new MapViewViewPoint(x, y);
177 }
178
179 /**
180 * Gets the {@link MapViewPoint} for the given {@link EastNorth} coordinate.
181 * @param eastNorth the position.
182 * @return The point for that position.
183 */
184 public MapViewPoint getPointFor(EastNorth eastNorth) {
185 return new MapViewEastNorthPoint(eastNorth);
186 }
187
188 /**
189 * Gets the {@link MapViewPoint} for the given {@link LatLon} coordinate.
190 * <p>
191 * This method exists to not break binary compatibility with old plugins
192 * @param latlon the position
193 * @return The point for that position.
194 * @since 10651
195 */
196 public MapViewPoint getPointFor(LatLon latlon) {
197 return getPointFor((ILatLon) latlon);
198 }
199
200 /**
201 * Gets the {@link MapViewPoint} for the given {@link LatLon} coordinate.
202 * @param latlon the position
203 * @return The point for that position.
204 * @since 12161
205 */
206 public MapViewPoint getPointFor(ILatLon latlon) {
207 try {
208 return getPointFor(Optional.ofNullable(latlon.getEastNorth(getProjection()))
209 .orElseThrow(IllegalArgumentException::new));
210 } catch (JosmRuntimeException | IllegalArgumentException | IllegalStateException e) {
211 throw BugReport.intercept(e).put("latlon", latlon);
212 }
213 }
214
215 /**
216 * Gets the {@link MapViewPoint} for the given node.
217 * This is faster than {@link #getPointFor(LatLon)} because it uses the node east/north cache.
218 * @param node The node
219 * @return The position of that node.
220 * @since 10827
221 */
222 public MapViewPoint getPointFor(Node node) {
223 return getPointFor((ILatLon) node);
224 }
225
226 /**
227 * Gets a rectangle representing the whole view area.
228 * @return The rectangle.
229 */
230 public MapViewRectangle getViewArea() {
231 return getForView(0, 0).rectTo(getForView(viewWidth, viewHeight));
232 }
233
234 /**
235 * Gets a rectangle of the view as map view area.
236 * @param rectangle The rectangle to get.
237 * @return The view area.
238 * @since 10827
239 */
240 public MapViewRectangle getViewArea(Rectangle2D rectangle) {
241 return getForView(rectangle.getMinX(), rectangle.getMinY()).rectTo(getForView(rectangle.getMaxX(), rectangle.getMaxY()));
242 }
243
244 /**
245 * Gets the center of the view.
246 * @return The center position.
247 */
248 public MapViewPoint getCenter() {
249 return getForView(viewWidth / 2.0, viewHeight / 2.0);
250 }
251
252 /**
253 * Gets the width of the view on the Screen;
254 * @return The width of the view component in screen pixel.
255 */
256 public double getViewWidth() {
257 return viewWidth;
258 }
259
260 /**
261 * Gets the height of the view on the Screen;
262 * @return The height of the view component in screen pixel.
263 */
264 public double getViewHeight() {
265 return viewHeight;
266 }
267
268 /**
269 * Gets the current projection used for the MapView.
270 * @return The projection.
271 * @see #getProjecting()
272 */
273 public Projection getProjection() {
274 return projecting.getBaseProjection();
275 }
276
277 /**
278 * Gets the current projecting instance that is used to convert between east/north and lat/lon space.
279 * @return The projection.
280 * @since 12161
281 */
282 public Projecting getProjecting() {
283 return projecting;
284 }
285
286 /**
287 * Creates an affine transform that is used to convert the east/north coordinates to view coordinates.
288 * @return The affine transform. It should not be changed.
289 * @since 10375
290 */
291 public AffineTransform getAffineTransform() {
292 return new AffineTransform(1.0 / scale, 0.0, 0.0, -1.0 / scale, -topLeft.east() / scale,
293 topLeft.north() / scale);
294 }
295
296 /**
297 * Gets a rectangle that is several pixel bigger than the view. It is used to define the view clipping.
298 * @return The rectangle.
299 */
300 public MapViewRectangle getViewClipRectangle() {
301 return getForView(-CLIP_BOUNDS, -CLIP_BOUNDS).rectTo(getForView(getViewWidth() + CLIP_BOUNDS, getViewHeight() + CLIP_BOUNDS));
302 }
303
304 /**
305 * Returns the area for the given bounds.
306 * @param bounds bounds
307 * @return the area for the given bounds
308 */
309 public Area getArea(Bounds bounds) {
310 Path2D area = new Path2D.Double();
311 getProjection().visitOutline(bounds, en -> {
312 MapViewPoint point = getPointFor(en);
313 if (area.getCurrentPoint() == null) {
314 area.moveTo(point.getInViewX(), point.getInViewY());
315 } else {
316 area.lineTo(point.getInViewX(), point.getInViewY());
317 }
318 });
319 area.closePath();
320 return new Area(area);
321 }
322
323 /**
324 * Creates a new state that is the same as the current state except for that it is using a new center.
325 * @param newCenter The new center coordinate.
326 * @return The new state.
327 * @since 10375
328 */
329 public MapViewState usingCenter(EastNorth newCenter) {
330 return movedTo(getCenter(), newCenter);
331 }
332
333 /**
334 * @param mapViewPoint The reference point.
335 * @param newEastNorthThere The east/north coordinate that should be there.
336 * @return The new state.
337 * @since 10375
338 */
339 public MapViewState movedTo(MapViewPoint mapViewPoint, EastNorth newEastNorthThere) {
340 EastNorth delta = newEastNorthThere.subtract(mapViewPoint.getEastNorth());
341 if (delta.distanceSq(0, 0) < .1e-20) {
342 return this;
343 } else {
344 return new MapViewState(topLeft.add(delta), this);
345 }
346 }
347
348 /**
349 * Creates a new state that is the same as the current state except for that it is using a new scale.
350 * @param newScale The new scale to use.
351 * @return The new state.
352 * @since 10375
353 */
354 public MapViewState usingScale(double newScale) {
355 return new MapViewState(newScale, this);
356 }
357
358 /**
359 * Creates a new state that is the same as the current state except for that it is using the location of the given component.
360 * <p>
361 * The view is moved so that the center is the same as the old center.
362 * @param positon The new location to use.
363 * @return The new state.
364 * @since 10375
365 */
366 public MapViewState usingLocation(JComponent positon) {
367 EastNorth center = this.getCenter().getEastNorth();
368 return new MapViewState(positon, this).usingCenter(center);
369 }
370
371 /**
372 * Creates a state that uses the projection.
373 * @param projection The projection to use.
374 * @return The new state.
375 * @since 10486
376 */
377 public MapViewState usingProjection(Projection projection) {
378 if (projection.equals(this.projecting)) {
379 return this;
380 } else {
381 return new MapViewState(projection, this);
382 }
383 }
384
385 /**
386 * Create the default {@link MapViewState} object for the given map view. The screen position won't be set so that this method can be used
387 * before the view was added to the hierarchy.
388 * @param width The view width
389 * @param height The view height
390 * @return The state
391 * @since 10375
392 */
393 public static MapViewState createDefaultState(int width, int height) {
394 Projection projection = Main.getProjection();
395 double scale = projection.getDefaultZoomInPPD();
396 MapViewState state = new MapViewState(projection, width, height, scale, new EastNorth(0, 0));
397 EastNorth center = calculateDefaultCenter();
398 return state.movedTo(state.getCenter(), center);
399 }
400
401 private static EastNorth calculateDefaultCenter() {
402 Bounds b = Optional.ofNullable(DownloadDialog.getSavedDownloadBounds()).orElseGet(
403 () -> Main.getProjection().getWorldBoundsLatLon());
404 return b.getCenter().getEastNorth(Main.getProjection());
405 }
406
407 /**
408 * Check if this MapViewState equals another one, disregarding the position
409 * of the JOSM window on screen.
410 * @param other the other MapViewState
411 * @return true if the other MapViewState has the same size, scale, position and projection,
412 * false otherwise
413 */
414 public boolean equalsInWindow(MapViewState other) {
415 return other != null &&
416 this.viewWidth == other.viewWidth &&
417 this.viewHeight == other.viewHeight &&
418 this.scale == other.scale &&
419 Objects.equals(this.topLeft, other.topLeft) &&
420 Objects.equals(this.projecting, other.projecting);
421 }
422
423 /**
424 * A class representing a point in the map view. It allows to convert between the different coordinate systems.
425 * @author Michael Zangl
426 */
427 public abstract class MapViewPoint {
428 /**
429 * Gets the map view state this path is used for.
430 * @return The state.
431 * @since 12505
432 */
433 public MapViewState getMapViewState() {
434 return MapViewState.this;
435 }
436
437 /**
438 * Get this point in view coordinates.
439 * @return The point in view coordinates.
440 */
441 public Point2D getInView() {
442 return new Point2D.Double(getInViewX(), getInViewY());
443 }
444
445 /**
446 * Get the x coordinate in view space without creating an intermediate object.
447 * @return The x coordinate
448 * @since 10827
449 */
450 public abstract double getInViewX();
451
452 /**
453 * Get the y coordinate in view space without creating an intermediate object.
454 * @return The y coordinate
455 * @since 10827
456 */
457 public abstract double getInViewY();
458
459 /**
460 * Convert this point to window coordinates.
461 * @return The point in window coordinates.
462 */
463 public Point2D getInWindow() {
464 return getUsingCorner(topLeftInWindow);
465 }
466
467 /**
468 * Convert this point to screen coordinates.
469 * @return The point in screen coordinates.
470 */
471 public Point2D getOnScreen() {
472 return getUsingCorner(topLeftOnScreen);
473 }
474
475 private Double getUsingCorner(Point corner) {
476 return new Point2D.Double(corner.getX() + getInViewX(), corner.getY() + getInViewY());
477 }
478
479 /**
480 * Gets the {@link EastNorth} coordinate of this point.
481 * @return The east/north coordinate.
482 */
483 public EastNorth getEastNorth() {
484 return new EastNorth(topLeft.east() + getInViewX() * scale, topLeft.north() - getInViewY() * scale);
485 }
486
487 /**
488 * Create a rectangle from this to the other point.
489 * @param other The other point. Needs to be of the same {@link MapViewState}
490 * @return A rectangle.
491 */
492 public MapViewRectangle rectTo(MapViewPoint other) {
493 return new MapViewRectangle(this, other);
494 }
495
496 /**
497 * Gets the current position in LatLon coordinates according to the current projection.
498 * @return The positon as LatLon.
499 * @see #getLatLonClamped()
500 */
501 public LatLon getLatLon() {
502 return projecting.getBaseProjection().eastNorth2latlon(getEastNorth());
503 }
504
505 /**
506 * Gets the latlon coordinate clamped to the current world area.
507 * @return The lat/lon coordinate
508 * @since 10805
509 */
510 public LatLon getLatLonClamped() {
511 return projecting.eastNorth2latlonClamped(getEastNorth());
512 }
513
514 /**
515 * Add the given offset to this point
516 * @param en The offset in east/north space.
517 * @return The new point
518 * @since 10651
519 */
520 public MapViewPoint add(EastNorth en) {
521 return new MapViewEastNorthPoint(getEastNorth().add(en));
522 }
523
524 /**
525 * Check if this point is inside the view bounds.
526 *
527 * This is the case iff <code>getOutsideRectangleFlags(getViewArea())</code> returns no flags
528 * @return true if it is.
529 * @since 10827
530 */
531 public boolean isInView() {
532 return inRange(getInViewX(), 0, getViewWidth()) && inRange(getInViewY(), 0, getViewHeight());
533 }
534
535 private boolean inRange(double val, int min, double max) {
536 return val >= min && val < max;
537 }
538
539 /**
540 * Gets the direction in which this point is outside of the given view rectangle.
541 * @param rect The rectangle to check agains.
542 * @return The direction in which it is outside of the view, as OUTSIDE_... flags.
543 * @since 10827
544 */
545 public int getOutsideRectangleFlags(MapViewRectangle rect) {
546 Rectangle2D bounds = rect.getInView();
547 int flags = 0;
548 if (getInViewX() < bounds.getMinX()) {
549 flags |= OUTSIDE_LEFT;
550 } else if (getInViewX() > bounds.getMaxX()) {
551 flags |= OUTSIDE_RIGHT;
552 }
553 if (getInViewY() < bounds.getMinY()) {
554 flags |= OUTSIDE_TOP;
555 } else if (getInViewY() > bounds.getMaxY()) {
556 flags |= OUTSIDE_BOTTOM;
557 }
558
559 return flags;
560 }
561
562 /**
563 * Gets the sum of the x/y view distances between the points. |x1 - x2| + |y1 - y2|
564 * @param p2 The other point
565 * @return The norm
566 * @since 10827
567 */
568 public double oneNormInView(MapViewPoint p2) {
569 return Math.abs(getInViewX() - p2.getInViewX()) + Math.abs(getInViewY() - p2.getInViewY());
570 }
571
572 /**
573 * Gets the squared distance between this point and an other point.
574 * @param p2 The other point
575 * @return The squared distance.
576 * @since 10827
577 */
578 public double distanceToInViewSq(MapViewPoint p2) {
579 double dx = getInViewX() - p2.getInViewX();
580 double dy = getInViewY() - p2.getInViewY();
581 return dx * dx + dy * dy;
582 }
583
584 /**
585 * Gets the distance between this point and an other point.
586 * @param p2 The other point
587 * @return The distance.
588 * @since 10827
589 */
590 public double distanceToInView(MapViewPoint p2) {
591 return Math.sqrt(distanceToInViewSq(p2));
592 }
593
594 /**
595 * Do a linear interpolation to the other point
596 * @param p1 The other point
597 * @param i The interpolation factor. 0 is at the current point, 1 at the other point.
598 * @return The new point
599 * @since 10874
600 */
601 public MapViewPoint interpolate(MapViewPoint p1, double i) {
602 return new MapViewViewPoint((1 - i) * getInViewX() + i * p1.getInViewX(), (1 - i) * getInViewY() + i * p1.getInViewY());
603 }
604 }
605
606 private class MapViewViewPoint extends MapViewPoint {
607 private final double x;
608 private final double y;
609
610 MapViewViewPoint(double x, double y) {
611 this.x = x;
612 this.y = y;
613 }
614
615 @Override
616 public double getInViewX() {
617 return x;
618 }
619
620 @Override
621 public double getInViewY() {
622 return y;
623 }
624
625 @Override
626 public String toString() {
627 return "MapViewViewPoint [x=" + x + ", y=" + y + ']';
628 }
629 }
630
631 private class MapViewEastNorthPoint extends MapViewPoint {
632
633 private final EastNorth eastNorth;
634
635 MapViewEastNorthPoint(EastNorth eastNorth) {
636 this.eastNorth = Objects.requireNonNull(eastNorth, "eastNorth");
637 }
638
639 @Override
640 public double getInViewX() {
641 return (eastNorth.east() - topLeft.east()) / scale;
642 }
643
644 @Override
645 public double getInViewY() {
646 return (topLeft.north() - eastNorth.north()) / scale;
647 }
648
649 @Override
650 public EastNorth getEastNorth() {
651 return eastNorth;
652 }
653
654 @Override
655 public String toString() {
656 return "MapViewEastNorthPoint [eastNorth=" + eastNorth + ']';
657 }
658 }
659
660 /**
661 * A rectangle on the MapView. It is rectangular in screen / EastNorth space.
662 * @author Michael Zangl
663 */
664 public class MapViewRectangle {
665 private final MapViewPoint p1;
666 private final MapViewPoint p2;
667
668 /**
669 * Create a new MapViewRectangle
670 * @param p1 The first point to use
671 * @param p2 The second point to use.
672 */
673 MapViewRectangle(MapViewPoint p1, MapViewPoint p2) {
674 this.p1 = p1;
675 this.p2 = p2;
676 }
677
678 /**
679 * Gets the projection bounds for this rectangle.
680 * @return The projection bounds.
681 */
682 public ProjectionBounds getProjectionBounds() {
683 ProjectionBounds b = new ProjectionBounds(p1.getEastNorth());
684 b.extend(p2.getEastNorth());
685 return b;
686 }
687
688 /**
689 * Gets a rough estimate of the bounds by assuming lat/lon are parallel to x/y.
690 * @return The bounds computed by converting the corners of this rectangle.
691 * @see #getLatLonBoundsBox()
692 */
693 public Bounds getCornerBounds() {
694 Bounds b = new Bounds(p1.getLatLon());
695 b.extend(p2.getLatLon());
696 return b;
697 }
698
699 /**
700 * Gets the real bounds that enclose this rectangle.
701 * This is computed respecting that the borders of this rectangle may not be a straignt line in latlon coordinates.
702 * @return The bounds.
703 * @since 10458
704 */
705 public Bounds getLatLonBoundsBox() {
706 // TODO @michael2402: Use hillclimb.
707 return projecting.getBaseProjection().getLatLonBoundsBox(getProjectionBounds());
708 }
709
710 /**
711 * Gets this rectangle on the screen.
712 * @return The rectangle.
713 * @since 10651
714 */
715 public Rectangle2D getInView() {
716 double x1 = p1.getInViewX();
717 double y1 = p1.getInViewY();
718 double x2 = p2.getInViewX();
719 double y2 = p2.getInViewY();
720 return new Rectangle2D.Double(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2));
721 }
722
723 /**
724 * Check if the rectangle intersects the map view area.
725 * @return <code>true</code> if it intersects.
726 * @since 10827
727 */
728 public boolean isInView() {
729 return getInView().intersects(getViewArea().getInView());
730 }
731
732 /**
733 * Gets the entry point at which a line between start and end enters the current view.
734 * @param start The start
735 * @param end The end
736 * @return The entry point or <code>null</code> if the line does not intersect this view.
737 */
738 public MapViewPoint getLineEntry(MapViewPoint start, MapViewPoint end) {
739 ProjectionBounds bounds = getProjectionBounds();
740 if (bounds.contains(start.getEastNorth())) {
741 return start;
742 }
743
744 double dx = end.getEastNorth().east() - start.getEastNorth().east();
745 double boundX = dx > 0 ? bounds.minEast : bounds.maxEast;
746 EastNorth borderIntersection = Geometry.getSegmentSegmentIntersection(start.getEastNorth(), end.getEastNorth(),
747 new EastNorth(boundX, bounds.minNorth),
748 new EastNorth(boundX, bounds.maxNorth));
749 if (borderIntersection != null) {
750 return getPointFor(borderIntersection);
751 }
752
753 double dy = end.getEastNorth().north() - start.getEastNorth().north();
754 double boundY = dy > 0 ? bounds.minNorth : bounds.maxNorth;
755 borderIntersection = Geometry.getSegmentSegmentIntersection(start.getEastNorth(), end.getEastNorth(),
756 new EastNorth(bounds.minEast, boundY),
757 new EastNorth(bounds.maxEast, boundY));
758 if (borderIntersection != null) {
759 return getPointFor(borderIntersection);
760 }
761
762 return null;
763 }
764 }
765
766}
Note: See TracBrowser for help on using the repository browser.