Set Map Boundaries For Kivy Garden MapView - python

I am using Garden-MapView in my Kivy app.
My issue is that the user can move outside the bounds of the map (pulled from OpenStreetMap) and continue to pan into the surrounding blue 'sea' area. Image below:
I'm not sure if the issue is specific to Garden-MapView, or if a generic kivy/widget answer could solve it.
My best attempt to solve this (out of many) is some crude code posted below. When the map extents move past the edge of the screen, the code calculates the center screen coordinate and pulls the center of the map to it. It works better for longitude. But this can slow down the app significantly due to the frequency of on_map_relocated event calls. I have also set MapView.min_zoom = 2:
class CustMapView(MapView):
def on_map_relocated(self, *kwargs):
x1, y1, x2, y2 = self.get_bbox()
centerX, centerY = Window.center
latRemainder = self.get_latlon_at(centerX, centerY, zoom=self.zoom)[0]-(x1+x2)/2
if x1 < -85.8: self.center_on((x1+x2)/2+latRemainder+.01, self.lon)
if x2 > 83.6: self.center_on((x1+x2)/2+latRemainder-.01, self.lon)
if y1 == -180: self.center_on(self.lat, (y1+y2)/2+0.01)
if y2 == 180: self.center_on(self.lat, (y1+y2)/2-0.01)
Full code to reproduce yourselves: https://pastebin.com/xX0GtPUb

Related

Getting desired angles from HoughLineP Transformations with OpenCV

I am trying to use probabilistic HoughLine Tranforms
.
After using HoughLinesP I get the following lines:
Note that the blue thing denoted in the image is not part of it. I have made it using paint for demonstration purposes. I want the angles shown in blue.
So what I tried was using the points from the lines and calculated slope of it and took the arctan. But I did not get any result. Note that the following function is a part of a class.
def HoughLines(self):
self.copy_image = self.img.copy()
minLineLength = 10
maxLineGap = 30
self.lines = cv2.HoughLinesP(self.edges,1,np.pi/180,15,minLineLength=minLineLength,maxLineGap=maxLineGap)
for line in range(0, len(self.lines)):
for x1,y1,x2,y2 in self.lines[line]:
cv2.line(self.copy_image,(x1,y1),(x2,y2),(0,255,0),2)
# cv2.imshow('hough',self.copy_image)
# cv2.imwrite('test.jpg', self.copy_image)
# cv2.waitKey(0)
angle = 0.0
self.nlines = self.lines.size
for x1, y1, x2, y2 in self.lines[0]:
angle += np.arctan2(y2 - y1, x2 - x1)
print(angle)
Therefore, I am stuck and I do not how to proceed. What can be a possible solution?
Any help is appreciated. Thank You.

Detect speed of few cars from video- Python

I'm writing a program that use speed camera (with Raspberry Pi Camera Board)
And should take photos of cars which speed is higher than given one, and send this photos to a policeman that waiting down the road, and save the number of the car.
I have a video that records from this angle:
Angle of capturing
I have a few problems:
There are many cars that driving at the same time, and I need to get info about all of them
I can't calculate the speed- maybe the problem occurs because I don't know how to deal with a few cars together
I have tried the algorithm that described here, but maybe I don't understand it well:
Find the speed of the vehicle from images
That's the part of my code where I try to detect the speed:
* In the code I detect a point at the centre of the car and following it while the car moving, I understand that I need to get X, Y points and X1, Y1 points, the time between them, and make a calculation.
But when i use it: print('centre',centroid[0:2]) i get only one point (x).
def update_vehicle(self, vehicle, matches):
# Find if any of the matches fits this vehicle
for i, match in enumerate(matches):
contour, centroid = match
vector = self.get_vector(vehicle.last_position, centroid)
if self.is_valid_vector(vector):
vehicle.add_position(centroid)
self.log.debug("Added match (%d, %d) to vehicle #%d. vector=(%0.2f,%0.2f)"
, centroid[0], centroid[1], vehicle.id, vector[0], vector[1])
#i detect the centre of the car and follow it, I've thought, how can I use the info of points of the centre and
#use it to detect the car, the problem is: I can't understand how to take this info,
#i need the X & Y point,
print(centre',centroid[0:2])
return i
# No matches fit...
vehicle.frames_since_seen += 1
self.log.debug("No match for vehicle #%d. frames_since_seen=%d"
, vehicle.id, vehicle.frames_since_seen)
return None
and this is the function of centroid that i use there:
def get_centroid(x, y, w, h):
x1 = int(w / 2)
y1 = int(h / 2)
cx = x + x1
cy = y + y1
return (cx, cy)

I'm having trouble projecting 3D coordinates to 2D screen coordinates

NOTE: Because I am a new member and I apparently need 10 reputation to post images or more than 2 links, I will be referring to this imgur album for visuals. Sorry for any inconvenience.
I'm trying to make a program to visualize 3D objects in python, but I'm having a problem with the function that projects a 3D coordinate onto 2D screen coordinates. It works in some cases, but not all. When the camera is relatively far away from the point and about on the same level, the projection looks very nice, such as in this image depicting a decagon and rectangle, whose vertices are defined by 3D points (Figure 2). However, when the camera is looking down on the points, the object they depict is flattened disproportionately (Figure 3), and when the camera is close to the points it must project, the shape gets warped (Figure 4).
The function in question is Camera.projectPoint, and I have included all of the functions and classes that it uses in the following:
class Vector3:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
self.mag = math.sqrt(x**2+y**2+z**2)
self.yaw = math.atan2(self.y, self.x)
self.pitch = math.atan2(self.z, math.sqrt(self.x**2+self.y**2))
class Point:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def vectorTo(self, p):
return(Vector3(p.x-self.x, p.y-self.y, p.z-self.z))
class Camera:
def __init__(self, pos, yaw, pitch, FOV=math.pi/2):
self.pos = pos
self.yaw = yaw
self.pitch = pitch
self.FOV = FOV
def projectPoint(self, point):
## finding the vector from the camera position to the point
v = self.pos.vectorTo(point)
## setting alpha to the difference in yaw between the camera and the vector to the point
## and beta to the difference in pitch
alpha = v.yaw - self.yaw
beta = v.pitch - self.pitch
## making sure that the smallest angle between the two is chosen
## (difference between 300 degrees and 10 degrees is 70, not 290)
alpha = (alpha+math.pi)%(2*math.pi)-math.pi
beta = (beta+math.pi)%(2*math.pi)-math.pi
## Doing the operation pictured in the diagram
h = math.sin(self.FOV/2)/math.cos(self.FOV/2)
x1 = math.sin(alpha)/math.cos(alpha)
y1 = math.sin(beta)/math.cos(beta)
## adjusting for screen resolution
return(x1*1000/h+500, y1*1000/h+325)
I have looked around for algorithms to project a 3D coordinate onto the screen, and have found a lot of things such as what is depicted in Figure 1 (which is what my function is based off of), but it doesn't seem to be working very well. Is it a problem with the algorithm? The way I implimented it? One of the functions it uses? (I'm like 99% sure all of the other functions and classes are perfectly fine). Any ideas on what's wrong and how to fix it? Thank you.

Hex grid map with PyQt4

I am trying to create a map editor. I intend the map to be an hexagonal grid where each hexagon is a tile of the map. A tile will be a graphical representation of that area (sea, meadow, desert, mountain, etc). The map is intended to be of any size. Let's freeze the requirements here for now :)
I want to use PyQt4 (take it as a design requirement). As I am just starting with Qt/PyQt, I am facing the problem of vastness: so big this Qt thing that I cannot grasp it all. And here I am, asking for your kind and most welcome experience.
After a bit of googling, I've decided to use the QGraphicalView/Scene approach. In fact, I was thinking about creating my own hexgrid class inheriting from QGraphicalView and creating my RegularPolygon class inheriting from QGraphicalPolygonItem.
Now they come the doubts and problems.
My main doubt is "Is my approach a correct one?" Think about the needs I have explained at the beginning of the post: hexagonal map, where each hexagon will be a tile of a given type (sea, desert, meadows, mountains, etc). I am concerned about performance once the editor works (scrolling will feel nice? and this kind of things).
And so far, the problem is about precision. I am drawing the hexgrid by creating and drawing all its hexagons (this even sounds bad to me... thinking about performance). I used some formulas to calculate the vertices of each hexagon and creating the polygon from there. I expect the sides of two consecutive hexagons to coincide exactly at the same location, but the rounding seems to be playing a bit with my desires, as sometimes the hexagon sides perfectly matches in the same location (good) and sometimes they do not match by what seems to be 1 pixel difference (bad). This gives a poor visual impression of the grid. Maybe I have not explained myself quite well... it's better if I give you the code and you run it by yourselves
So summarizing:
Do you think my approach will give future performance issues?
Why are not the hexagons placed exactly so that they share sides? How to avoid this problem?
The code:
#!/usr/bin/python
"""
Editor of the map.
"""
__meta__ = \
{
(0,0,1): (
[ "Creation" ],
[ ("Victor Garcia","vichor#xxxxxxx.xxx") ]
)
}
import sys, math
from PyQt4 import QtCore, QtGui
# ==============================================================================
class HexGrid(QtGui.QGraphicsView):
"""
Graphics view for an hex grid.
"""
# --------------------------------------------------------------------------
def __init__(self, rect=None, parent=None):
"""
Initializes an hex grid. This object will be a GraphicsView and it will
also handle its corresponding GraphicsScene.
rect -- rectangle for the graphics scene.
parent -- parent widget
"""
super(HexGrid,self).__init__(parent)
self.scene = QtGui.QGraphicsScene(self)
if rect != None:
if isinstance(rect, QtCore.QRectF): self.scene.setSceneRect(rect)
else: raise StandardError ('Parameter rect should be QtCore.QRectF')
self.setScene(self.scene)
# ==============================================================================
class QRegularPolygon(QtGui.QGraphicsPolygonItem):
"""
Regular polygon of N sides
"""
def __init__(self, sides, radius, center, angle = None, parent=None):
"""
Initializes an hexagon of the given radius.
sides -- sides of the regular polygon
radius -- radius of the external circle
center -- QPointF containing the center
angle -- offset angle in radians for the vertices
"""
super(QRegularPolygon,self).__init__(parent)
if sides < 3:
raise StandardError ('A regular polygon at least has 3 sides.')
self._sides = sides
self._radius = radius
if angle != None: self._angle = angle
else: self._angle = 0.0
self._center = center
points = list()
for s in range(self._sides):
angle = self._angle + (2*math.pi * s/self._sides)
x = center.x() + (radius * math.cos(angle))
y = center.y() + (radius * math.sin(angle))
points.append(QtCore.QPointF(x,y))
self.setPolygon( QtGui.QPolygonF(points) )
# ==============================================================================
def main():
"""
That's it: the main function
"""
app = QtGui.QApplication(sys.argv)
grid = HexGrid(QtCore.QRectF(0.0, 0.0, 500.0, 500.0))
radius = 50
sides = 6
apothem = radius * math.cos(math.pi/sides)
side = 2 * apothem * math.tan(math.pi/sides)
xinit = 50
yinit = 50
angle = math.pi/2
polygons = list()
for x in range(xinit,xinit+20):
timesx = x - xinit
xcenter = x + (2*apothem)*timesx
for y in range(yinit, yinit+20):
timesy = y - yinit
ycenter = y + ((2*radius)+side)*timesy
center1 = QtCore.QPointF(xcenter,ycenter)
center2 = QtCore.QPointF(xcenter+apothem,ycenter+radius+(side/2))
h1 = QRegularPolygon(sides, radius, center1, angle)
h2 = QRegularPolygon(sides, radius, center2, angle)
# adding polygons to a list to avoid losing them when outside the
# scope (loop?). Anyway, just in case
polygons.append(h1)
polygons.append(h2)
grid.scene.addItem(h1)
grid.scene.addItem(h2)
grid.show()
app.exec_()
# ==============================================================================
if __name__ == '__main__':
main()
and last but not least, sorry for the long post :)
Thanks
Victor
Personally, I'd define each hexagonal tile as a separate SVG image, and use QImage and QSvgRenderer classes to render them to QPixmaps (with an alpha channel) whenever the zoom level changes. I'd create a QGraphicsItem subclass for displaying each tile.
The trick is to pick the zoom level so that the width of the (upright) hexagon is a multiple of two, and the height a multiple of four, with width/height approximately sqrt(3/4). The hexagons are slightly squished in either direction, but for all hexagons at least eight pixels in diameter, the effect is inperceptible.
If the width of the hexagon is 2*w, and height 4*h, here's how to map the (upright) hexagons to Cartesian coordinates:
If each side of the hexagon is a, then h=a/2 and w=a*sqrt(3)/2, therefore w/h=sqrt(3).
For optimum display quality, pick integer w and h, so that their ratio is approximately sqrt(3) ≃ 1.732. This means your hexagons will be very slightly squished, but that's okay; it is not perceptible.
Because the coordinates are now always integers, you can safely (without display artefacts) use pre-rendered hexagon tiles, as long as they have an alpha channel, and perhaps a border to allow smoother alpha transitions. Each rectangular tile is then 2*w+2*b pixels wide and 4*h+2*b pixels tall, where b is the number of extra border (overlapping) pixels.
The extra border is needed to avoid perceptible seams (background color bleeding through) where pixels are only partially opaque in all overlapping tiles. The border allows you to better blend the tile into the neighboring tile; something the SVG renderer will do automatically if you include a small border region in your SVG tiles.
If you use typical screen coordinates where x grows right and y down, then the coordinates for hexagon X,Y relative to the 0,0 one are trivial:
y = 3*h*Y
if Y is even, then:
x = 2*w*X
else:
x = 2*w*X + w
Obviously, odd rows of hexagons are positioned half a hexagon to the right.
Subclassing QGraphicsItem and using a bounding polygon (for mouse and interaction tests) means Qt will do all the heavy work for you, when you wish to know which hexagonal tile the mouse is hovering on top of.
However, you can do the inverse mapping -- from screen coordinates back to hexagons -- yourself.
First, you calculate which rectangular grid cell (green grid lines in the image above) the coordinate pair is in:
u = int(x / w)
v = int(y / h)
Let's assume all coordinates are nonnegative. Otherwise, % must be read as "nonnegative remainder, when divided by". (That is, 0 <= a % b < b for all a, even negative a; b is always a positive integer here.)
If the origin is as shown in the above image, then two rows out of every three are trivial, except that every odd row of hexagons is shifted one grid cell right:
if v % 3 >= 1:
if v % 6 >= 4:
X = int((u - 1) / 2)
Y = int(v / 3)
else:
X = int(u / 2)
Y = int(v / 3)
Every third row contains rectangular grid cells with a diagonal boundary, but worry not: if the boundary is \ (wrt. above image), you only need to check if
(x % w) * h >= (y % h) * w
to find out if you are in the upper right triangular part. If the boundary is / wrt. above image, you only need to check if
(x % w) * h + (y % h) * w >= (w * h - (w + h) / 2)
to find out if you are in the lower right triangular part.
In each four-column and six-row section of rectangular grid cells, there are eight cases that need to be handled, using one of the above test clauses. (I'm too lazy to work the exact if clauses for you here; like I said, I'd let Qt do that for me.) This rectangular region repeats exactly for the entire hexagonal map; thus, a full coordinate conversion may need up to 9 if clauses (depending on how you write it), so it's a bit annoying to write.
If you wish to determine e.g. the mouse cursor location relative to the hexagon it is hovering over, first use the above to determine which hexagon the mouse hovers over, then substract the coordinates of that hexagon from the mouse coordinates to get the coordinates relative to the current hexagon.
Try with this main() function. I used the radius of the inscribed circle (ri) instead of the circumscribed circle that you used (radius). It looks a bit better now, but still not perfect. I think the way the oblique sides are drawn at the top and bottom of the hexagon are different.
def main():
"""
That's it: the main function
"""
app = QtGui.QApplication(sys.argv)
grid = HexGrid(QtCore.QRectF(0.0, 0.0, 500.0, 500.0))
radius = 50 # circumscribed circle radius
ri = int(radius / 2 * math.sqrt(3)) # inscribed circle radius
sides = 6
apothem = int(ri * math.cos(math.pi/sides))
side = int(2 * apothem * math.tan(math.pi/sides))
xinit = 50
yinit = 50
angle = math.pi/2
polygons = list()
for x in range(xinit,xinit+20):
timesx = x - xinit
xcenter = x + (2*apothem-1)*timesx
for y in range(yinit, yinit+20):
timesy = y - yinit
ycenter = y + ((2*ri)+side)*timesy
center1 = QtCore.QPointF(xcenter,ycenter)
center2 = QtCore.QPointF(xcenter+apothem,ycenter+ri+(side/2))
h1 = QRegularPolygon(sides, ri, center1, angle)
h2 = QRegularPolygon(sides, ri, center2, angle)
# adding polygons to a list to avoid losing them when outside the
# scope (loop?). Anyway, just in case
polygons.append(h1)
polygons.append(h2)
grid.scene.addItem(h1)
grid.scene.addItem(h2)
grid.show()
app.exec_()
There are multiple problems here. They aren't specifically related to Qt or to Python, but to general computer science.
You have floating point geometrical shapes that you want to display on a raster device, so somehow there has to be a floating point to integer conversion. It's not in your code, so it will happen at a lower level: in the graphics library, the display driver or whatever. Since you're not happy with the result, you have to handle this conversion yourself.
There's no right or wrong way to do this. For example, take your case of a hex tile that has a “radius” of 50. The hexagon is oriented so that the W vertex is at (-50,0) and the E vertex is at (50,0). Now the NE vertex of this hexagon is at approximately (25,0,43.3). The hexagon that's adjacent to this one in the N direction has its center at about y=86.6 and its top edge at 129.9. How would you like to pixellate this? If you round 43.3 down to 43, now you no longer have a mathematically exact regular hexagon. If you round 129.9 up to 130, your first hexagon is 86 pixels in total height but the one on top of it is 87. This is an issue that you must resolve based on the project's requirements.
And this is just one case (radius=50). If you allow the radius to be variable, can you come up with an algorithm to handle all cases? I couldn't. I think you need to use a fixed screen dimension for your hexagons, or at least reduce the possibilities to a small number.
Nowhere in your code do you determine the size of the display window, so I don't understand how you intend to handle scaling issues, or determine how many hexes are needed to show the full map.
As to your first question, I am certain that the performance will be poor. The constructor for QRegularPolygon is inside the loop that creates the hexes, so it gets called many times (800 in your example). It does two trig calculations for each vertex, so you perform 9600 trig calculations as you build your list of hexes. You don't need ANY of them. The calculations are the sine and cosine of 0 degrees, 60 degrees, 120 degrees and so on. Those are so easy you don't even need sin and cos.
The use of the trig functions exacerbates the floating point/integer problem, too. Look at this:
>> int(50.0*math.sin(math.pi/6))
24
We know it should be 25, but the computer figures it as int(24.999999999996) – I may have left out a few 9's.
If you calculate the vertex positions of just one hexagon, you can get all the other ones by a simple translation. See the useful Qt functions QPolygon->translate or QPolygon->translated.
It seems that you don't need a constructor that can handle any type of polygon when your design concept absolutely needs hexagons. Did you just copy it from somewhere? I think it's mostly clutter, which always opens the door to errors.
Do you really need polygons here? Later on, I suppose, the game will use raster images, so the polygons are just for display purposes.
You could just take a point cloud representing all corners of the polygon and draw lines beneath them. With this, you avoid problems of rounding / floating point arithmetics etc.

Python (Pygame) return angle in specific interval

I have this code:
def getAngle(x1, y1, x2, y2):
rise = y1 - y2
run = x1 - x2
angle = math.atan2(run, rise) # get the angle in radians
angle = angle * (180 / math.pi) # convert to degrees
angle = (angle) % 360 # adjust for a right-facing sprite
return angle
... which is returning an angle depending on mouse position on the screen.
I want to set an interval where rotation of my object will stop at the specific point. For an example: if angle is bigger than 90° i want my object to stop getting higher angle. In this case 90° should be like some border where rotation stops.
I think i need 2 conditions becouse angle shouldn't be higher from 90° on the left and right.
Anyone got an idea how to solve that?
this part of the code is in the game loop (it uses defined getAngle):
mousex, mousey = pygame.mouse.get_pos()
for cannonx, cannony in (((width/2)-45, height-25), ((width/2)-45, height-25)):
degrees = getAngle(cannonx, cannony, mousex, mousey)
rotcannonImg = pygame.transform.rotate(cannonImg, degrees)
rotcannonRect = rotcannonImg.get_rect()
rotcannonRect.center = (cannonx, cannony)
windowSurface.blit(rotcannonImg, rotcannonRect)
The phrase “angle shouldn't be higher from 90° on the left and right” does not have a well-defined meaning in English, so I am not sure what you intend. However, the following figure shows the relations between various angles and the signs of rise and run when point (x1, y1) is located where the lines cross and (x2, y2) is in particular octants. Note, dy = rise, dx = run. That is, you might be able to test signs of rise and run to get the information you want.

Categories

Resources