问题
I'll first introduce the problem: I'm developing an application where I've to show a map field, and overlay markers and lines. However, in BlacBerry OS 5.0, the only MapField
available in the API does not provide means to overlay stuff, only show the map at a location. It also provides methods to transform screen coordinates (pixels) to/from WGS84 coordinates. These methods might be computationally expensive.
So to paint my own items, I need to extend this class and override its paint() method. The extending class will also hold a collection of locations.This is how the overriden method would look like (I'll use Java here):
public void paint (Graphics g) {
super.paint(g); //draws the map
//TODO
//Draw placemarks. The placemarks are basically holder objects
//(for latitude and longitude) stored in a collection in this class.
}
However, to paint these objects in the screen we should first convert the placemark locations (lat, long) to screen coordinates (x,y in pixels). This cannot be done in advance since the map is not static, so it is able to scroll and zoom in an out. This is why in each paint loop we should paint at least THE VISIBLE OBJECTS. That said, my question is:
Given a rectangular perimeter where the corners are geographic locations (the transformed four corners of the portion of the map currently displayed), is there a fast method to loop on every placemark in the collection and determine if they are visible or not?
I don't need this test to be 100% accurate, I don't mind if a few locations outside the screen are painted. But as the placemark collection can contain many elements (< 100), and the paint method will be called on each screen repaint, trying to paint every location in the collection without checking if it is visible or not could affect performance and introduce lag when the user interacts with the map.
Before you try to provide a naive answer, notice that this is not a simple geometrical problem: We're working with geographic coordinates, not with integer screen coordinates. The world doesn't end in longitude +180, or in latitude +90. This function should work in the poles and in the ecuator, so I need it to work also when we have a transition line (from -180 to +180, or from -90 to +90, or both lines) intersecting with the rectangle. As the logic can get complex, I'd like to know if there's an existing algorithm or open source library where this has already been done and tested, rather than implementing my own one.
I could also first convert every location in the collection to screen coordinates, and then easily check against a rectangle composed only by positive screen coordinates (starting from x=0, y=0), but as the transformation functions might be expensive, I think it is better to transform only 4 points in each refresh (the visible map corners) than an indeterminate number of placemarks.
Any other approach or idea would also be appreciated.
Thanks in advance.
回答1:
Just a primitive idea: Take two opposite corners of your "rectangle", e.g. the upper left one and the lower right one. Transform both corners to Cartesian space coordinate (x,y,z)
by:
x = cos[long] cos[lat]
y = sin[long] cos[lat]
z = sin[lat]
Both of the (x,y,z)
coordinates are unit vectors (imagine the center of the sphere being in (0,0,0)
, and the vectors being arrows from there to the surface). Find the "middle" of your map area as the normed average of the two corner vectors (add as vectors, then divide by length of sum vector to make sure you have a new unit vector). When you have the middle (xMiddle,yMiddle,zMiddle)
, for every placemark coordinate transformed into cartesian (x,y,z)
, use the dot product with (xMiddle,yMiddle,zMiddle)
as a measure of the closeness to the middle.
Now include every placemark whose dot product with (xMiddle,yMiddle,zMiddle)
is greater than the upper left corner's dot product with (xMiddle,yMiddle,zMiddle)
.
This should give you all placemarks inside a circular disk centered at "middle".
回答2:
You could at least rule out a large number of candidates with a naive box check in map coordinate space. There are probably three main cases here. Either there's a pole is in the rectangle or there isn't. If the pole isn't visible, either the rectangle crosses the +/-180 degrees line or not. There can't be any +/-90 line, because that would put together north and south pole and you're not working with a 4D map, are you? ;-)
Case 1, a pole is visible: If it's the north pole, figure out which of the corners has the smallest latitude. Any latitude smaller than that is likely off-screen. If it's the south pole just reverse the logic, i.e. use the largest latitude and exclude any items with a greater latitude. I know, having the pole in one corner and the equator in another means you still include an entire hemisphere. But at least you can cheaply exclude the other half.
Case 2, no pole, not crossing the +/-180 longitude line: Find the min/max longitude and latitude values and use those for a simple box check. Anything outside the box is off-screen.
Case 3, no pole, but crossing the +/-180 longitude line: Same as above for latitude. For longitude, find the longitudes furthest away from +180 and -180 respectively. Exclude any items with a latitude outside the min/max or between the two furthest longitudes you found.
The cases 2 and 3 should be able to rule out enough candidates to make a brute-force check for the others feasible. Case 1 may require further post-processing, but I'm afraid that part is a bit too complicated for me if you want something sophisticated. I suppose you could somehow find an off-screen point closest to the pole if the pole is further away from the center of the screen. Then somehow construct a triangle-like shape with one corner at that point and make it as large as possible without touching the screen rectangle.
回答3:
I think you just need to Convert your rectengular perimeter to geographic coordinates. Instead of trying to convert the geogrpahic coordinates to screen coordinates.
I'm sorry that the answer is naive - but you do you ask about logic. And so the logical thing I visualize is a window sliding on a spehere and that means you need to have that window geographical coordinates as your points of reference.
After you process that "3d" information you can start rendering your view.
来源:https://stackoverflow.com/questions/10632457/efficient-way-of-checking-if-a-location-is-inside-a-rectangular-perimeter