diff --git a/.gitignore b/.gitignore
index 922918e..7f15977 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,9 @@
__pycache__/
*.py[cod]
+# Ctags
+tags
+
# C extensions
*.so
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..daa7862
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1 @@
+include pytilemap/*.png
diff --git a/README.md b/README.md
index f5d2dae..643355e 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,36 @@
# PyTileMap
-Tile map visualization for PyQt4.
+PyQt Map System forked from:
-This project allows the visualization of Open Street Map, HERE and other maps
-in a PyQt4 GUI.
+https://github.com/allebacco/PyTileMap
-The project has been inspired from of https://github.com/emka/python-lightmaps.
+Simple to use map widget that can be used in a PyQt GUI.
+
+# Install:
+python setup.py install
+
+# Example:
+
+examples/main_gs.py
+
+# Map Widgets
+
+- MapGraphicsLineItem - a line between two lat/lon points
+- MapGraphicsPolyLineItem - lines between a series of lat/lon points
+- MapGraphicsPixmapItem - place a pixmap at a given lat/lon (does not scale with map zoom)
+- MapGraphicsTextItem - place text at a given lat/lon
+- MapGraphicsLinesGroupItem - a group of lines
+- MapGraphicsCircleItem - draw a circle centered at a lat/lon
+- MapGraphicsRectItem - draw a rectangle given upper left and lower right lat/lon (scales with zoom)
+- MapGraphicsRectShapeItem - draw a rectangle with a fixed with and height (does not scale with zoom)
+- MapGraphicsGeoSvgItem - draw an svg given upper left and lower right lat/lon (scales with zoom)
+- MapGraphicsGeoPixmapItem - draw a pixmap given upper left and lower right lat/lon (scales with zoom)
+- MapGraphicsGeoPixmapItemCorners - draw a pixmap given all four lat/lon corners (scales with zoom)
+- MapGraphicsLabelItem - a label that is attached to another map item
+
+# Screenshot
+
+
+# Other Python Maps
+
+https://github.com/TomSchimansky/TkinterMapView
diff --git a/example/main_gs.py b/example/main_gs.py
index 33904d5..051aa1a 100644
--- a/example/main_gs.py
+++ b/example/main_gs.py
@@ -8,6 +8,7 @@
from pytilemap import MapGraphicsView, MapTileSourceHere, MapTileSourceOSM
+
POINTS = [(44.837632, 10.201736),
(44.837621, 10.201474),
(44.837594, 10.201205),
@@ -77,7 +78,7 @@ def __init__(self):
self.setCentralWidget(view)
- view.scene().setCenter(10.065990, 44.861041)
+ view.scene().setCenter(10.065990, 44.861041, zoom=13)
view.setOptimizationFlag(QGraphicsView.DontSavePainterState, True)
view.setRenderHint(QPainter.Antialiasing, True)
view.setRenderHint(QPainter.SmoothPixmapTransform, True)
@@ -111,13 +112,46 @@ def __init__(self):
pointItemPixmapOrigin = view.scene().addCircle(10.090598, 44.857893, 3.0)
pointItemPixmapOrigin.setBrush(Qt.black)
- pointItemWithChild = view.scene().addCircle(10.083103, 44.858014, 3.0)
- pointItemWithChild.setBrush(Qt.blue)
- pointItemWithChild.setPen(QPen(Qt.NoPen))
+ # Pixmap with both corners geo-referenced
+ geo_pixmap = QPixmap(36,36)
+ geo_pixmap.fill(Qt.blue)
+ geo_pixmap_item= view.scene().addGeoPixmap(10.090598, 44.8709, 10.092, 44.873, geo_pixmap)
+ geo_pixmap_item.setLabel("GeoPixmapItem")
+ geo_pixmap_item.showLabel()
+
+ # Blue Point with an HTML label
+ blue_point = view.scene().addCircle(10.083103, 44.868014, 3.0)
+ blue_point.setBrush(Qt.blue)
+ blue_point.setPen(QPen(Qt.NoPen))
+ blue_point.setLabel("
" + "Label for Blue Point" + "
", html=True)
+ blue_point.showLabel()
+
+ # Location Pin
+ pin_item = view.scene().addPin(10.06, 44.84)
+ pin_item.setLabel("Pin Item
",html=True)
+ pin_item.showLabel()
+
+ # Pixmap with all four corners geo-referenced
+ lon0r = 10.06
+ lat0r = 44.83
+ lon1r = 10.110000000000001
+ lat1r = 44.743397459621555
+ lon2r = 9.936794919243113
+ lat2r = 44.64339745962155
+ lon3r = 9.886794919243112
+ lat3r = 44.73
+
+ clr = QColor(0,255,0,100)
+ pix = QPixmap(100,100)
+ pix.fill(clr)
+
+ view.scene().addGeoPixmapCorners(lon0r, lat0r,
+ lon1r, lat1r,
+ lon2r, lat2r,
+ lon3r, lat3r,
+ pix)
+
- textItem = QGraphicsSimpleTextItem('Annotation\nfor blue point', parent=pointItemWithChild)
- textItem.setBrush(QBrush(QColor(Qt.blue)))
- textItem.setPos(-5, 3)
lats_2 = list()
lons_2 = list()
@@ -134,8 +168,12 @@ def __init__(self):
legendItem.addRect('Sphere 4', '#00FFFF', border=None)
legendItem.addPoint('Polygon 5', '#FF00FF', border=None)
+ navItem = view.scene().addNavItem(anchor=Qt.TopRightCorner)
+
scaleItem = view.scene().addScale(anchor=Qt.BottomRightCorner)
+
+
def main():
w = MapZoom()
diff --git a/pytilemap/__init__.py b/pytilemap/__init__.py
index 133b0cc..42a075f 100644
--- a/pytilemap/__init__.py
+++ b/pytilemap/__init__.py
@@ -2,13 +2,15 @@
from .mapscene import MapGraphicsScene
from .mapview import MapGraphicsView
-from .mapitems import MapGraphicsCircleItem, MapGraphicsLineItem, \
+from .mapitems import MapItem, MapGraphicsCircleItem, MapGraphicsLineItem, \
MapGraphicsPolylineItem, MapGraphicsPixmapItem, MapGraphicsTextItem, \
MapGraphicsRectItem
from .maplegenditem import MapLegendItem
from .mapescaleitem import MapScaleItem
from .maptilesources import MapTileSource, MapTileSourceHere, MapTileSourceHereDemo, \
MapTileSourceOSM, MapTileSourceHTTP
+from .mapnavitem import MapNavItem
+from .imagebutton import ImageButton
__all__ = [
@@ -26,6 +28,9 @@
'MapTileSourceHereDemo',
'MapTileSourceOSM',
'MapTileSourceHTTP',
+ "MapNavItem",
+ "ImageButton",
+ "MapItem"
]
__version__ = '1.0.0'
diff --git a/pytilemap/imagebutton.py b/pytilemap/imagebutton.py
new file mode 100644
index 0000000..ffd1803
--- /dev/null
+++ b/pytilemap/imagebutton.py
@@ -0,0 +1,39 @@
+from __future__ import print_function, absolute_import
+
+from qtpy.QtCore import Qt, Signal
+from qtpy.QtWidgets import QGraphicsObject, QGraphicsItemGroup, QGraphicsPixmapItem
+
+class ImageButton(QGraphicsObject):
+ '''
+ Custom Image Holder Class that Allows for event handling -
+ - QGraphicsPixmapItem is not a QObject, so cannot catch events.
+ '''
+ QtParentClass = QGraphicsObject
+
+ clicked = Signal(int)
+
+ def __init__(self, img, parent=None):
+ '''
+ Args:
+ img: A QPixmap instance
+
+ Keyword Args:
+ parent: A pyqt window instance
+ '''
+ QGraphicsObject.__init__(self, parent=parent)
+ self.image = QGraphicsPixmapItem(img, parent=self)
+ self.parent = parent
+
+ def paint(self, painter, option, widget):
+ self.image.paint(painter,option,widget)
+
+ def boundingRect(self):
+ return self.image.boundingRect()
+
+ def mouseReleaseEvent(self, event):
+ self.clicked.emit(1)
+
+ def mousePressEvent(self, event):
+ pass
+# end ImageButton
+
diff --git a/pytilemap/mapitems.py b/pytilemap/mapitems.py
index 581bda9..b949845 100644
--- a/pytilemap/mapitems.py
+++ b/pytilemap/mapitems.py
@@ -2,11 +2,14 @@
import numpy as np
-from qtpy.QtCore import Qt, QLineF, QPointF, QRectF
-from qtpy.QtGui import QPainterPath
+from qtpy.QtCore import Qt, QLineF, QPointF, QRectF, QSize
+from qtpy.QtGui import QPainterPath, QPen, QBrush, QColor, QTransform, QPolygonF
from qtpy.QtWidgets import QGraphicsEllipseItem, QGraphicsLineItem, \
QGraphicsPathItem, QGraphicsPixmapItem, QGraphicsItemGroup, \
- QGraphicsSimpleTextItem, QGraphicsItem, QGraphicsRectItem
+ QGraphicsSimpleTextItem, QGraphicsItem, QGraphicsRectItem, QGraphicsTextItem, QMenu, QAction
+
+#from qtpy.QtWidgets import QGraphicsSvgItem
+from qtpy.QtSvg import QGraphicsSvgItem, QSvgRenderer
from .functions import iterRange, makePen, izip
from .qtsupport import getQVariantValue
@@ -24,10 +27,15 @@ class MapItem(object):
QtParentClass = None
+
def __init__(self):
if not isinstance(self, QGraphicsItem):
raise RuntimeError('MapItem must be an instance of QGraphicsItem')
+ self._label = "label"
+ self._label_item = None
+ self._label_html = False
+
def itemChange(self, change, value):
if change == self.ItemSceneChange:
# Disconnect the old scene, if any
@@ -79,6 +87,52 @@ def setZoom(self, zoom):
def updatePosition(self, scene):
raise NotImplementedError()
+ def setLabel(self, label, html=False):
+ self._label = label
+ self._label_html = html
+
+ def getLabelLocation(self):
+ ''' Get label location for this object
+
+ Args:
+ none
+
+ Returns:
+ (pos x, pos y) : position of label in pixels
+ '''
+ rect = self.getGeoRect()
+ br = rect.bottomRight()
+ pos = (br.x(), br.y())
+ return pos
+
+ def getGeoRect(self):
+ ''' Get bounding rectangle for this obj
+
+ Args:
+ none
+
+ Returns:
+ QRectF: (px x, px y, w, h)
+ '''
+ return self.boundingRect()
+
+ def showLabel(self):
+ ''' Show label for this object, if html is indicated, display formatted'''
+ if self._label_item:
+ return
+ self._label_item = MapGraphicsLabelItem(self, self._label)
+ if self._label_html:
+ self._label_item.setHtml(self._label)
+ self.scene().addItem(self._label_item)
+
+ def hideLabel(self):
+ ''' Hide label for this object'''
+ if not self._label_item:
+ return
+ self.scene().removeItem(self._label_item)
+ self._label_item = None
+
+
class MapGraphicsCircleItem(QGraphicsEllipseItem, MapItem):
"""Circle item for the MapGraphicsScene
@@ -101,9 +155,10 @@ def __init__(self, longitude, latitude, radius, parent=None):
"""
QGraphicsEllipseItem.__init__(self, parent=parent)
MapItem.__init__(self)
+ self.setFlags(QGraphicsItem.ItemIsMovable)
- self._lon = longitude
- self._lat = latitude
+ self._lon = longitude
+ self._lat = latitude
self._radius = radius
def updatePosition(self, scene):
@@ -130,6 +185,8 @@ def setLonLat(self, longitude, latitude):
scene = self.scene()
if scene is not None:
self.updatePosition(scene)
+ if self._label_item:
+ self._label_item.updatePosition(scene)
def setRadius(self, radius):
self._radius = radius
@@ -137,6 +194,58 @@ def setRadius(self, radius):
if scene is not None:
self.updatePosition(scene)
+ def hideLabel(self):
+ if not self._label_item:
+ return
+ self.scene().removeItem(self._label_item)
+ self._label_item = None
+
+class MapGraphicsRectShapeItem(QGraphicsRectItem, MapItem):
+ """Circle item for the MapGraphicsScene
+ """
+
+ QtParentClass = QGraphicsRectItem
+
+ def __init__(self, lon, lat, width, height, parent=None):
+ """Constructor.
+
+ Args:
+ lon0(float): Longitude of the center point
+ lat0(float): Latitude of the center point
+ width(int): width in pixels
+ height(int): height in pixels
+ parent(QGraphicsItem): Parent item, default None.
+
+ """
+ QGraphicsRectItem.__init__(self, parent=parent)
+ MapItem.__init__(self)
+
+ self._lon = lon
+ self._lat = lat
+ self._width = width
+ self._height = height
+
+ def updatePosition(self, scene):
+ """Update the position of the circle.
+
+ Args:
+ scene(MapGraphicsScene): Scene to which the circle belongs.
+ """
+ pos = scene.posFromLonLat(self._lon, self._lat)
+
+ self.prepareGeometryChange()
+ # This object is centered on the lat lon point, so shift it by half width/height
+ rect = QRectF(pos[0]-self._height//2, pos[1]-self._width//2, self._width, self._height)
+ self.setRect(rect)
+ self.setPos(QPointF(0.0, 0.0))
+
+ def setLonLat(self, lon, lat):
+ self._lon = lon
+ self._lat = lat
+ scene = self.scene()
+ if scene is not None:
+ self.updatePosition(self.scene())
+
class MapGraphicsRectItem(QGraphicsRectItem, MapItem):
"""Circle item for the MapGraphicsScene
@@ -173,9 +282,11 @@ def updatePosition(self, scene):
"""
pos0 = scene.posFromLonLat(self._lon0, self._lat0)
pos1 = scene.posFromLonLat(self._lon1, self._lat1)
+ width = abs(int(pos1[0] - pos0[0]))
+ height= abs(int(pos0[1] - pos1[1]))
self.prepareGeometryChange()
- rect = QRectF(pos0, pos1).normalized()
+ rect = QRectF(pos0[0], pos0[1], width, height)
self.setRect(rect)
self.setPos(QPointF(0.0, 0.0))
@@ -221,6 +332,7 @@ def setLonLat(self, lon0, lat0, lon1, lat1):
self.updatePosition(self.scene())
+
class MapGraphicsPolylineItem(QGraphicsPathItem, MapItem):
QtParentClass = QGraphicsPathItem
@@ -257,6 +369,307 @@ def setLonLat(self, longitudes, latitudes):
if scene is not None:
self.updatePosition(scene)
+class MapGraphicsGeoSvgItem(QGraphicsSvgItem, MapItem):
+
+ QtParentClass = QGraphicsSvgItem
+
+ def __init__(self, lon0, lat0, lon1, lat1, svg_filename, parent=None):
+ """Constructor.
+
+ Args:
+ longitude(float): Longitude of the upper left corner
+ latitude(float): Latitude of the upper left corner
+ longitude(float): Longitude of the lower right corner
+ latitude(float): Latitude of the lower right corner
+ svg_filename: Svg file name
+ scene(MapGraphicsScene): Scene the item belongs to.
+ parent(QGraphicsItem): Parent item.
+
+ This will display an svg file with the corners geo-registered
+ """
+ QGraphicsSvgItem.__init__(self, svg_filename, parent=parent)
+ MapItem.__init__(self)
+
+ self._lon0 = lon0
+ self._lat0 = lat0
+ self._lon1 = lon1
+ self._lat1 = lat1
+ self._xsize = 0
+ self._ysize = 0
+
+ self.x_mult = 1
+ self.y_mult = 1
+ self._renderer = QSvgRenderer(svg_filename);
+ self._border = QGraphicsRectItem(parent=self)
+ self._border.setPen(Qt.black)
+
+ def updatePosition(self, scene):
+ pos0 = scene.posFromLonLat(self._lon0, self._lat0)
+ pos1 = scene.posFromLonLat(self._lon1, self._lat1)
+ self.prepareGeometryChange()
+ xsize = abs(int(pos1[0] - pos0[0]))
+ ysize = abs(int(pos0[1] - pos1[1]))
+
+ rect = scene.sceneRect()
+ x = rect.x()
+ y = rect.y()
+ width = rect.width()
+ height = rect.height()
+ self.ul_x = min(pos0[0], pos1[0])
+ self.ul_y = min(pos0[1], pos1[1])
+ self.lr_x = max(pos0[0], pos1[0])
+ self.lr_y = max(pos0[1], pos1[1])
+ #self.scale(width, height)
+
+ #print ("screen rect: {0}:{1}, {2}:{3}".format(int(x), int(x+width), int(y), int(y+height)),
+ # "img rect: {0}:{1}, {2}:{3}".format(int(self.ul_x), int(self.lr_x), int(self.ul_y), int(self.lr_y)))
+
+ #if xsize != self._xsize or ysize != self._ysize:
+ self._xsize = xsize
+ self._ysize = ysize
+ self.ul_x = min(pos0[0], pos1[0])
+ self.ul_y = min(pos0[1], pos1[1])
+ self.setPos(self.ul_x, self.ul_y)
+
+ # Scaled approach - does weird smoothing
+ def paint(self, painter, option, widget=None):
+ #print (self.x_mult, self.y_mult, self.orig_pixmap.width(), self.orig_pixmap.height())
+ self._renderer.render(painter, QRectF(0,0, self._xsize, self._ysize))
+
+ def boundingRect(self):
+ return QRectF(0, 0, self._xsize, self._ysize)
+
+ def getGeoRect(self):
+ ''' get geo referenced rectangle for this object
+
+ Returns:
+ QRectF (upper left x, upper left y, width, height)
+ '''
+ pos0 = self.scene().posFromLonLat(self._lon0, self._lat0)
+ pos1 = self.scene().posFromLonLat(self._lon1, self._lat1)
+ xsize = abs(int(pos1[0] - pos0[0]))
+ ysize = abs(int(pos0[1] - pos1[1]))
+ ul_x = min(pos0[0], pos1[0])
+ ul_y = min(pos0[1], pos1[1])
+ rect = QRectF(ul_x, ul_y, xsize, ysize)
+ return rect
+
+
+ def setLonLat(self, lon0, lat0, lon1, lat1):
+ self._lon0 = lon0
+ self._lat0 = lat0
+ self._lon1 = lon1
+ self._lat1 = lat1
+ scene = self.scene()
+ if scene is not None:
+ self.updatePosition(self.scene())
+
+# end MapGraphicsGeoSvg
+
+class MapGraphicsGeoPixmapItemCorners(QGraphicsPixmapItem, MapItem):
+ '''
+ A pixmap that has all 4 corners specified so it warps to the map
+ '''
+
+ QtParentClass = QGraphicsPixmapItem
+
+ def __init__(self, lon0, lat0, lon1, lat1,
+ lon2, lat2, lon3, lat3, pixmap, parent=None):
+ """Constructor.
+
+ Args:
+ lon0(float): Longitude (decimal degrees) of the upper left corner of the image
+ lat0(float): Latitude of the upper left corner of the image
+ lon1(float): longitude of the next point clockwise
+ lat0(float): latitude of the next point clockwise
+ lon2(float): longitude of the next point clockwise
+ lat2(float): longitude of the next point clockwise
+ lon3(float): latitude of the next point clockwise
+ lat3(float): latitude of the next point clockwise
+ pixmap(QPixmap): Pixmap.
+ scene(MapGraphicsScene): Scene the item belongs to.
+ parent(QGraphicsItem): Parent item.
+
+ Show a pixamp with geo-registered corners
+ """
+ QGraphicsPixmapItem.__init__(self, parent=parent)
+ MapItem.__init__(self)
+
+ self._lon0 = lon0
+ self._lat0 = lat0
+ self._lon1 = lon1
+ self._lat1 = lat1
+ self._lon2 = lon2
+ self._lat2 = lat2
+ self._lon3 = lon3
+ self._lat3 = lat3
+ self._xsize = 0
+ self._ysize = 0
+ self.setPixmap(pixmap)
+ self.setShapeMode(1)
+ self.x_mult = 1
+ self.y_mult = 1
+
+ def updatePosition(self, scene):
+
+ # 1. Get pix coords for each lat/lon point
+ pos0 = scene.posFromLonLat(self._lon0, self._lat0)
+ pos1 = scene.posFromLonLat(self._lon1, self._lat1)
+ pos2 = scene.posFromLonLat(self._lon2, self._lat2)
+ pos3 = scene.posFromLonLat(self._lon3, self._lat3)
+ self.prepareGeometryChange()
+
+ # Set the image to 0, 0, then use a transform to
+ # to translate, rotate and warp it to the map
+
+ # tranfsorm and scale
+ self.setPos(0, 0)
+ t = QTransform()
+ poly1 = QPolygonF()
+
+ w = self.pixmap().width()
+ h = self.pixmap().height()
+
+ poly1.append(QPointF( 0, 0 ))
+ poly1.append(QPointF( w, 0 ))
+ poly1.append(QPointF( w, h ))
+ poly1.append(QPointF( 0, h ))
+
+ poly2 = QPolygonF()
+ poly2.append(QPointF(pos0[0], pos0[1]))
+ poly2.append(QPointF(pos1[0], pos1[1]))
+ poly2.append(QPointF(pos2[0], pos2[1]))
+ poly2.append(QPointF(pos3[0], pos3[1]))
+ success = QTransform.quadToQuad(poly1, poly2, t)
+ if not success:
+ logging.error('Unable to register image')
+
+ self.setTransform(t)
+
+
+ def getGeoRect(self):
+ ''' get geo referenced rectangle for this object
+
+ Returns:
+ QRectF (upper left x, upper left y, width, height)
+ '''
+ pos0 = self.scene().posFromLonLat(self._lon0, self._lat0)
+ pos1 = self.scene().posFromLonLat(self._lon1, self._lat1)
+ xsize = abs(int(pos1[0] - pos0[0]))
+ ysize = abs(int(pos0[1] - pos1[1]))
+ ul_x = min(pos0[0], pos1[0])
+ ul_y = min(pos0[1], pos1[1])
+ rect = QRectF(ul_x, ul_y, xsize, ysize)
+ return rect
+
+
+ def setLonLat(self, lon0, lat0, lon1, lat1):
+ self._lon0 = lon0
+ self._lat0 = lat0
+ self._lon1 = lon1
+ self._lat1 = lat1
+ scene = self.scene()
+ if scene is not None:
+ self.updatePosition(self.scene())
+
+# end MapGraphicsGeoPixmap
+
+
+
+
+class MapGraphicsGeoPixmapItem(QGraphicsPixmapItem, MapItem):
+
+ QtParentClass = QGraphicsPixmapItem
+
+ def __init__(self, lon0, lat0, lon1, lat1, pixmap, parent=None):
+ """Constructor.
+
+ Args:
+ longitude(float): Longitude of the upper left corner
+ latitude(float): Latitude of the upper left corner
+ longitude(float): Longitude of the lower right corner
+ latitude(float): Latitude of the lower right corner
+ pixmap(QPixmap): Pixmap.
+ scene(MapGraphicsScene): Scene the item belongs to.
+ parent(QGraphicsItem): Parent item.
+
+ Show a pixamp with geo-registered corners
+ """
+ QGraphicsPixmapItem.__init__(self, parent=parent)
+ MapItem.__init__(self)
+
+ self._lon0 = lon0
+ self._lat0 = lat0
+ self._lon1 = lon1
+ self._lat1 = lat1
+ self._xsize = 0
+ self._ysize = 0
+
+ self.orig_pixmap = pixmap
+ self.setPixmap(pixmap)
+ self.setShapeMode(1)
+ self.x_mult = 1
+ self.y_mult = 1
+
+ def updatePosition(self, scene):
+ pos0 = scene.posFromLonLat(self._lon0, self._lat0)
+ pos1 = scene.posFromLonLat(self._lon1, self._lat1)
+ self.prepareGeometryChange()
+ xsize = abs(int(pos1[0] - pos0[0]))
+ ysize = abs(int(pos0[1] - pos1[1]))
+
+ rect = scene.sceneRect()
+ x = rect.x()
+ y = rect.y()
+ width = rect.width()
+ height = rect.height()
+ self.ul_x = min(pos0[0], pos1[0])
+ self.ul_y = min(pos0[1], pos1[1])
+ self.lr_x = max(pos0[0], pos1[0])
+ self.lr_y = max(pos0[1], pos1[1])
+
+
+ #if xsize != self._xsize or ysize != self._ysize:
+ self._xsize = xsize
+ self._ysize = ysize
+ self.x_mult = xsize / self.orig_pixmap.width()
+ self.y_mult = ysize / self.orig_pixmap.width()
+ if 1:
+ newscale = QSize(xsize, ysize)
+ scaled = self.orig_pixmap.scaled(newscale)
+ self.setPixmap(scaled)
+ self.ul_x = min(pos0[0], pos1[0])
+ self.ul_y = min(pos0[1], pos1[1])
+ self.setPos(self.ul_x, self.ul_y)
+
+
+ def getGeoRect(self):
+ ''' get geo referenced rectangle for this object
+
+ Returns:
+ QRectF (upper left x, upper left y, width, height)
+ '''
+ pos0 = self.scene().posFromLonLat(self._lon0, self._lat0)
+ pos1 = self.scene().posFromLonLat(self._lon1, self._lat1)
+ xsize = abs(int(pos1[0] - pos0[0]))
+ ysize = abs(int(pos0[1] - pos1[1]))
+ ul_x = min(pos0[0], pos1[0])
+ ul_y = min(pos0[1], pos1[1])
+ rect = QRectF(ul_x, ul_y, xsize, ysize)
+ return rect
+
+
+ def setLonLat(self, lon0, lat0, lon1, lat1):
+ self._lon0 = lon0
+ self._lat0 = lat0
+ self._lon1 = lon1
+ self._lat1 = lat1
+ scene = self.scene()
+ if scene is not None:
+ self.updatePosition(self.scene())
+
+# end MapGraphicsGeoPixmap
+
class MapGraphicsPixmapItem(QGraphicsPixmapItem, MapItem):
"""Item for showing a pixmap in a MapGraphicsScene.
@@ -268,7 +681,7 @@ def __init__(self, longitude, latitude, pixmap, parent=None):
"""Constructor.
Args:
- longitude(float): Longitude of the origin of the pixmap.
+ longitude(float): Longitude of the center of the pixmap
latitude(float): Latitude of the center of the pixmap.
pixmap(QPixmap): Pixmap.
scene(MapGraphicsScene): Scene the item belongs to.
@@ -281,6 +694,20 @@ def __init__(self, longitude, latitude, pixmap, parent=None):
self._lat = latitude
self.setPixmap(pixmap)
+ def getGeoRect(self):
+ ''' get geo referenced rectangle for this object
+
+ Returns:
+ QRectF (upper left x, upper left y, width, height)
+ '''
+ rect = self.boundingRect()
+ pos = self.scene().posFromLonLat(self._lon, self._lat)
+ w = rect.width()
+ h = rect.height()
+ rect2 = QRectF(pos[0], pos[1], w, h )
+ return rect2
+
+
def updatePosition(self, scene):
"""Update the origin position of the item.
@@ -291,7 +718,12 @@ def updatePosition(self, scene):
"""
pos = scene.posFromLonLat(self._lon, self._lat)
self.prepareGeometryChange()
- self.setPos(pos[0], pos[1])
+ rect = self.boundingRect()
+ w = rect.width()
+ h = rect.height()
+ self.setPos(pos[0] - h//2, pos[1] -w//2)
+ if self._label_item:
+ self._label_item.updatePosition(scene)
def setLonLat(self, longitude, latitude):
"""Update the origin coordinates of the item.
@@ -308,8 +740,7 @@ def setLonLat(self, longitude, latitude):
if scene is not None:
self.updatePosition(scene)
-
-class MapGraphicsTextItem(QGraphicsSimpleTextItem, MapItem):
+class MapGraphicsTextItem(QGraphicsTextItem, MapItem):
"""Text item for the MapGraphicsScene
"""
@@ -320,6 +751,9 @@ def __init__(self, longitude, latitude, text, parent=None, min_zoom_visibility=N
MapItem.__init__(self)
self._min_zoom = min_zoom_visibility
self._lon, self._lat = longitude, latitude
+ self._border = QGraphicsRectItem(parent=self)
+ self._border.setPen(QPen(Qt.NoPen))
+ self._border.setBrush(QBrush(QColor(190, 190, 190, 160)))
def resetMinZoomVisibility(self):
"""Delete level of zoom under which the text disappears. """
@@ -333,7 +767,39 @@ def updatePosition(self, scene):
"""Update the origin position of the item."""
pos = scene.posFromLonLat(self._lon, self._lat)
- self.setPos(pos)
+ self.setPos(pos[0], pos[1])
+ if self._min_zoom is not None:
+ self.setVisible(scene._zoom >= self._min_zoom)
+ #rect = super(MapGraphicsTextItem, self).boundingRect()
+ #self._border.setRect()
+
+class MapGraphicsLabelItem(QGraphicsTextItem, MapItem):
+ """ Label for an item - updates its position with the item
+ """
+
+ QtParentClass = QGraphicsSimpleTextItem
+
+ def __init__(self, other_item, text, parent=None, min_zoom_visibility=None):
+ QGraphicsSimpleTextItem.__init__(self, text, parent=parent)
+ MapItem.__init__(self)
+ self.other_item = other_item
+ self._min_zoom = min_zoom_visibility
+ self._border = QGraphicsRectItem(parent=self)
+ self._border.setPen(QPen(Qt.NoPen))
+ self._border.setBrush(QBrush(QColor(190, 190, 190, 160)))
+
+ def resetMinZoomVisibility(self):
+ """Delete level of zoom under which the text disappears. """
+ self._min_zoom = None
+
+ def setMinZoomVisibility(self, zoom_level):
+ """Update level of zoom under which the text disappears. """
+ self._min_zoom = zoom_level
+
+ def updatePosition(self, scene):
+ """Update the origin position of the item."""
+ pos = self.other_item.getLabelLocation()
+ self.setPos(pos[0], pos[1])
if self._min_zoom is not None:
self.setVisible(scene._zoom >= self._min_zoom)
@@ -380,7 +846,7 @@ def updatePosition(self, scene):
x, y = scene.posFromLonLat(self._longitudes, self._latitudes)
lines = self._lines
- for i in iterRange(0, len(lines)-1):
+ for i in iterRange(0, len(lines)):
lines[i].setLine(x[i], y[i], x[i+1], y[i+1])
def setLonLat(self, longitudes, latitudes):
@@ -407,3 +873,4 @@ def setLonLat(self, longitudes, latitudes):
def __getitem__(self, index):
return self._lines[index]
+
diff --git a/pytilemap/mapnavitem.py b/pytilemap/mapnavitem.py
new file mode 100644
index 0000000..1432e9f
--- /dev/null
+++ b/pytilemap/mapnavitem.py
@@ -0,0 +1,150 @@
+from __future__ import print_function, absolute_import
+
+import os
+from qtpy.QtCore import Qt, Slot, QRectF, QPointF, QObject, Signal
+from qtpy.QtGui import QPen, QBrush, QColor, QPixmap
+from qtpy.QtWidgets import QGraphicsObject, QGraphicsRectItem, QGraphicsItemGroup, \
+ QGraphicsSimpleTextItem, QGraphicsEllipseItem, QGraphicsLineItem, QGraphicsPixmapItem
+
+from .imagebutton import ImageButton
+from .mapitems import MapItem
+from .functions import makePen, makeBrush
+from .qtsupport import getQVariantValue
+
+from .maplegenditem import *
+
+
+class MapNavItem(QGraphicsObject, MapItem):
+
+ QtParentClass = QGraphicsObject
+
+ _posForAnchors = {
+ Qt.TopLeftCorner: QPointF(20.0, 75.0),
+ Qt.TopRightCorner: QPointF(40.0, -15.0),
+ Qt.BottomLeftCorner: QPointF(20.0, -15.0),
+ Qt.BottomRightCorner: QPointF(30.0, 75.0),
+ }
+
+
+ def __init__(self, anchor, parent=None):
+ QGraphicsObject.__init__(self, parent=parent)
+ MapItem.__init__(self)
+ self.setZValue(200.0)
+
+ anchorPos = self._posForAnchors[anchor]
+ self._anchorPos = QPointF(anchorPos)
+ self._anchor = anchor
+
+ self._border = QGraphicsRectItem(parent=self)
+ self._border.setPen(QPen(Qt.NoPen))
+ self._border.setBrush(QBrush(QColor(190, 190, 190, 160)))
+
+ self._entries = list()
+
+ imgfile = os.path.dirname(__file__) + os.sep + 'zoom_in_symbol.png'
+ img = QPixmap(24,24)
+ img.load(imgfile)
+ img = img.scaled(24,24)
+ img = ImageButton(img, parent=self)
+ self.zoom_in_button = img
+ self.addEntry(self.zoom_in_button)
+
+ imgfile = os.path.dirname(__file__) + os.sep + 'zoom_out_symbol.png'
+ img2 = QPixmap(24,24)
+ img2.load(imgfile)
+ img2 = img2.scaled(24,24)
+ img2 = ImageButton(img2, parent=self)
+ self.zoom_out_button = img2
+ self.addEntry(self.zoom_out_button)
+
+ def _sceneChanged(self, oldScene, newScene):
+ if oldScene is not None:
+ oldScene.sceneRectChanged.disconnect(self.setSceneRect)
+ if newScene is not None:
+ newScene.sceneRectChanged.connect(self.setSceneRect)
+ # Setup the new position of the item
+ self.setSceneRect(newScene.sceneRect())
+
+ def updatePosition(self, scene):
+ pass
+
+ def addRect(self, text, color, border=None, size=20.0):
+ shape = QGraphicsRectItem(size / 2.0, size / 2.0, size, size)
+ brush = makeBrush(color)
+ shape.setBrush(brush)
+ shape.setPen(makePen(border))
+
+ self.addEntry(MapLegendEntryItem(shape, text))
+
+ def addEntry(self, entry):
+ self._entries.append(entry)
+ self._updateLayout()
+
+ def boundingRect(self):
+ return self._border.boundingRect()
+
+ def paint(*args, **kwargs):
+ pass
+
+ @Slot(QRectF)
+ def setSceneRect(self, rect):
+ anchorPos = self._anchorPos
+ anchor = self._anchor
+ newPos = None
+ if anchor == Qt.BottomRightCorner:
+ newPos = rect.bottomRight() - anchorPos
+ elif anchor == Qt.TopRightCorner:
+ newPos = rect.topRight() - anchorPos
+ elif anchor == Qt.TopLeftCorner:
+ newPos = rect.topLeft() + anchorPos
+ elif anchor == Qt.BottomLeftCorner:
+ newPos = rect.bottomLeft() + anchorPos
+ else:
+ raise NotImplementedError('Other corner have not actually been implemented')
+
+ self.setPos(newPos)
+
+
+ def _updateLayout(self):
+ self.prepareGeometryChange()
+
+ bottom = 0.0
+ left = 0.0
+ right = 0.0
+ for entry in self._entries:
+ entry.setPos(left, bottom)
+ bottom += entry.boundingRect().bottom()
+ right = max(right, entry.boundingRect().right() + 1.0)
+
+ self._border.setRect(0.0, 0.0, right, bottom + 1.0)
+
+ def pen(self):
+ """Pen for the background of the legend
+
+ Returns:
+ QPen: Pen for the background of the legend
+ """
+ return self._border.pen()
+
+ def brush(self):
+ """Brush for the background of the legend
+
+ Returns:
+ QBrush: Brush for the background of the legend
+ """
+ return self._border.brush()
+
+ def setPen(self, *args, **kwargs):
+ """Set the pen for the background of the legend
+
+ The arguments are the same of the :func:`makePen` function
+ """
+ return self._border.setPen(makePen(*args, **kwargs))
+
+ def setBrush(self, *args, **kwargs):
+ """Set the brush for the background of the legend
+
+ The arguments are the same of the :func:`makeBrush` function
+ """
+ return self._border.setBrush(makeBrush(*args, **kwargs))
+
diff --git a/pytilemap/mapscene.py b/pytilemap/mapscene.py
index 604ea1e..accb283 100644
--- a/pytilemap/mapscene.py
+++ b/pytilemap/mapscene.py
@@ -1,16 +1,22 @@
from __future__ import print_function, absolute_import, division
from numpy import floor
+import math
+import os
-from qtpy.QtCore import Qt, Slot, Signal, QRect, QRectF, QPointF, QSizeF
-from qtpy.QtGui import QPixmap
-from qtpy.QtWidgets import QGraphicsScene
+from qtpy.QtCore import Qt, Slot, Signal, QRect, QRectF, QPointF, QSizeF, QPoint, QSize
+from qtpy.QtGui import QPixmap, QPen, QBrush, QColor, QPainter
+from qtpy.QtWidgets import QGraphicsScene, QGraphicsLineItem, QGraphicsRectItem, QGraphicsItem
+
+from qtpy.QtSvg import QGraphicsSvgItem
from .mapitems import MapGraphicsCircleItem, MapGraphicsLineItem, \
MapGraphicsPolylineItem, MapGraphicsPixmapItem, MapGraphicsTextItem, \
- MapGraphicsRectItem, MapGraphicsLinesGroupItem
+ MapGraphicsRectItem, MapGraphicsLinesGroupItem, MapGraphicsGeoPixmapItem, \
+ MapGraphicsLabelItem, MapGraphicsGeoSvgItem, MapGraphicsRectShapeItem, MapGraphicsGeoPixmapItemCorners
from .maplegenditem import MapLegendItem
from .mapescaleitem import MapScaleItem
+from .mapnavitem import MapNavItem
from .functions import iterRange
from .tileutils import posFromLonLat, lonLatFromPos
@@ -20,6 +26,8 @@ class MapGraphicsScene(QGraphicsScene):
"""
sigZoomChanged = Signal(int)
+ sigSelectionDrawn = Signal(float, float, float, float)
+ customSceneRectChanged = Signal(float,float,float,float)
def __init__(self, tileSource, parent=None):
"""Constructor.
@@ -30,7 +38,7 @@ def __init__(self, tileSource, parent=None):
"""
QGraphicsScene.__init__(self, parent=parent)
- self._zoom = 15
+ self._zoom = 8
self._tileSource = tileSource
self._tileSource.setParent(self)
@@ -48,6 +56,12 @@ def __init__(self, tileSource, parent=None):
self.setSceneRect(0.0, 0.0, 400, 300)
self.sceneRectChanged.connect(self.onSceneRectChanged)
+ # Rubberband Support for Drawing Areas
+ self.rect_start = None
+ self.rect_end = None
+ self.rubberband = None
+ self.rubberband_enabled = False
+
@Slot()
def close(self):
self._tileSource.close()
@@ -68,6 +82,63 @@ def setTileSource(self, newTileSource):
self.invalidate()
self.update()
+ def mousePressEvent(self, evt):
+ '''Catch right-click events for rectangle drawing'''
+ if self.rubberband_enabled and evt.button() == 2:
+ evt.accept()
+ pos = evt.scenePos()
+ self.rect_start = pos
+
+ if self.rubberband != None:
+ self.removeItem(self.rubberband)
+ self.rubberband = None
+
+ else:
+ evt.ignore()
+ QGraphicsScene.mousePressEvent(self, evt)
+
+ def mouseReleaseEvent(self, evt):
+ '''Catch right-click events for rectangle drawing'''
+ if self.rubberband_enabled and evt.button() == 2:
+ evt.accept()
+ pos = evt.scenePos()
+ lon0, lat0 = self.lonLatFromPos(self.rect_start.x(), self.rect_start.y())
+ lon1,lat1 = self.lonLatFromPos(pos.x(), pos.y())
+ self.removeItem(self.rubberband)
+
+ self.rect_start = None
+ self.rect_end = None
+ self.rubberband = None
+
+ self.sigSelectionDrawn.emit(lon0, lat0, lon1, lat1)
+
+ else:
+ evt.ignore()
+ QGraphicsScene.mouseReleaseEvent(self, evt)
+
+ def mouseMoveEvent(self, evt):
+ '''Catch right-click events for rectangle drawing'''
+ if self.rubberband_enabled and self.rect_start:
+ pos = evt.scenePos()
+ #lon,lat = self.lonLatFromPos(pos.x(), pos.y())
+ self.rect_end = pos
+ if not self.rubberband:
+ self.rubberband = QGraphicsRectItem(
+ min(self.rect_start.x(), self.rect_end.x()),
+ min(self.rect_start.y(), self.rect_end.y()),
+ abs(self.rect_end.x()-self.rect_start.x()),
+ abs(self.rect_end.y()-self.rect_start.y()))
+ clr = QColor(240,240,240,100)
+ self.rubberband.setBrush(clr)
+ self.rubberband.setPen(QPen(QBrush(Qt.blue), 1.0))
+ self.addItem(self.rubberband)
+ else:
+ self.rubberband.setRect(
+ min(self.rect_start.x(), self.rect_end.x()),
+ min(self.rect_start.y(), self.rect_end.y()),
+ abs(self.rect_end.x()-self.rect_start.x()),
+ abs(self.rect_end.y()-self.rect_start.y()))
+
@Slot(QRectF)
def onSceneRectChanged(self, rect):
"""Callback for the changing of the visible rect.
@@ -105,6 +176,10 @@ def onSceneRectChanged(self, rect):
self.invalidate()
self.update()
+ lon0, lat0 = self.lonLatFromPos(rect.x(), rect.y())
+ lon1, lat1 = self.lonLatFromPos(rect.x() + rect.width(), rect.y() + rect.height())
+
+ self.customSceneRectChanged.emit(lon0, lat0, lon1, lat1)
def drawBackground(self, painter, rect):
"""Draw the background tiles.
@@ -176,7 +251,7 @@ def zoomIn(self, pos=None):
current center position.
"""
if pos is None:
- pos = self.sceneRect().center()
+ pos = QPoint(self.width()/2, self.height()/2)
self.zoomTo(pos, self._zoom + 1)
def zoomOut(self, pos=None):
@@ -187,9 +262,17 @@ def zoomOut(self, pos=None):
current center position.
"""
if pos is None:
- pos = self.sceneRect().center()
+ pos = QPoint(self.width()/2, self.height()/2)
self.zoomTo(pos, self._zoom - 1)
+ @Slot()
+ def handleZoomIn(self):
+ self.zoomIn()
+
+ @Slot()
+ def handleZoomOut(self):
+ self.zoomOut()
+
def zoom(self):
return self._zoom
@@ -224,8 +307,8 @@ def requestTiles(self):
zoom = self._zoom
# Request load of new tiles
- for x in iterRange(numXtiles):
- for y in iterRange(numYtiles):
+ for x in iterRange(numXtiles+1):
+ for y in iterRange(numYtiles+1):
tp = (left + x, top + y)
# Request tile only if missing
if tp not in tilePixmaps:
@@ -260,7 +343,7 @@ def setSize(self, width, height):
rect = QRectF(self.sceneRect().topLeft(), QSizeF(width, height))
self.setSceneRect(rect)
- def setCenter(self, lon, lat):
+ def setCenter(self, lon, lat, zoom=None):
"""Move the center of the visible area to new coordinates.
Update the scene rect.
@@ -268,12 +351,17 @@ def setCenter(self, lon, lat):
Args:
lon(float): New longitude of the center.
lat(float): New latitude of the center.
+ zoom(int [1:15]): Zoom Level
"""
+ if zoom != None and zoom < 15 and zoom > 0:
+ self._zoom = zoom
+
rect = QRectF(self.sceneRect())
pos = self.posFromLonLat(lon, lat)
rect.moveCenter(QPointF(pos[0], pos[1]))
self.setSceneRect(rect)
+
def center(self):
centerPos = self.sceneRect().center()
centerCoord = self.lonLatFromPos(centerPos.x(), centerPos.y())
@@ -331,6 +419,23 @@ def tileFromPos(self, x, y):
tdim = float(self._tileSource.tileSize())
return QPointF(x / tdim, y / tdim)
+ def addRectShape(self, longitude, latitude, width, height):
+ """Add a new rectangle with fixed width/height
+
+ Args:
+ longitude(float): Longitude of the top left.
+ latitude(float): Latitude of the top left
+ width (float): width in pixels
+ height(float): height in pixels
+
+ Returns:
+ MapGraphicsCircleItem added to the scene.
+ """
+
+ item = MapGraphicsRectShapeItem(longitude, latitude, width, height)
+ self.addItem(item)
+ return item
+
def addCircle(self, longitude, latitude, radius):
"""Add a new circle to the graphics scene.
@@ -392,6 +497,23 @@ def addPolyline(self, longitudes, latitudes):
self.addItem(item)
return item
+ def addPin(self, lon, lat):
+ """Add a location pin to the graphics scene.
+
+ Args:
+ longitude(float): Longitude (decimal degrees WGS84) of the pin
+ latitude(float): Latitude of the Pin
+
+ Returns:
+ MapGraphicsPixmapItem added to the scene.
+ """
+ pinfile = os.path.dirname(__file__) + os.sep + 'red_pin.png'
+ pixmap = QPixmap()
+ pixmap.load(pinfile)
+ item = MapGraphicsPixmapItem(lon, lat, pixmap)
+ self.addItem(item)
+ return item
+
def addPixmap(self, longitude, latitude, pixmap):
"""Add a new circle (point) to the graphics scene.
@@ -411,6 +533,61 @@ def addPixmap(self, longitude, latitude, pixmap):
self.addItem(item)
return item
+ def addGeoSvg(self, lon0, lat0, lon1, lat1, svg):
+ '''Add a geo-registered pixmap to the scene
+
+ Args:
+ lon0(float): Longitude (decimal degress WGS84) upper left
+ lat0(float): Lattitude (decimal degrees WGS84) upper left
+ lon1(float): Longitude lower right
+ lat1(float): Lattitudelower right
+
+ Returns:
+ MapGraphicsGeoPixmapItem
+ '''
+ item = MapGraphicsGeoSvgItem(lon0, lat0, lon1, lat1, svg)
+ self.addItem(item)
+ return item
+
+
+ def addGeoPixmap(self, lon0, lat0, lon1, lat1, pixmap):
+ '''Add a geo-registered pixmap to the scene
+
+ Args:
+ lon0(float): Longitude (decimal degress WGS84) upper left
+ lat0(float): Lattitude (decimal degrees WGS84) upper left
+ lon1(float): Longitude lower right
+ lat1(float): Lattitudelower right
+
+ Returns:
+ MapGraphicsGeoPixmapItem
+ '''
+ item = MapGraphicsGeoPixmapItem(lon0, lat0, lon1, lat1, pixmap)
+ self.addItem(item)
+ return item
+
+ def addGeoPixmapCorners(self, lon0, lat0, lon1, lat1, lon2, lat2, lon3, lat3, pixmap):
+ '''Add a geo-registered pixmap to the scene using 4 lat-lon corners
+
+ Args:
+ lon0(float): Longitude (decimal degress WGS84) upper left of image
+ lat0(float): Lattitude (decimal degrees WGS84) upper left of image
+ lon1(float): Lat of next point clockwise
+ lat1(float): Lon of next point clockwise
+ lon2(float): Lat of next point clockwise
+ lat2(float): Lon of next point clockwise
+ lon3(float): Lat of next point clockwise
+ lat3(float): Lon of next point clockwise
+
+ Returns:
+ MapGraphicsGeoPixmapItem
+ '''
+ item = MapGraphicsGeoPixmapItemCorners(lon0, lat0, lon1, lat1,
+ lon2, lat2, lon3, lat3, pixmap)
+ self.addItem(item)
+ return item
+
+
def addText(self, longitude, latitude, text):
"""Add a test item to the graphics scene.
@@ -425,6 +602,13 @@ def addText(self, longitude, latitude, text):
self.addItem(item)
return item
+ def addNavItem(self, anchor):
+ self.nav_item = MapNavItem(anchor)
+ self.addItem(self.nav_item)
+ self.nav_item.zoom_in_button.clicked.connect(self.handleZoomIn)
+ self.nav_item.zoom_out_button.clicked.connect(self.handleZoomOut)
+ return self.nav_item
+
def addLegend(self, pos=QPointF(10.0, 10.0)):
legend = MapLegendItem(pos=pos)
self.addItem(legend)
@@ -450,6 +634,7 @@ def addScale(self, **kwargs):
self.addItem(scaleItem)
return scaleItem
+
def addLinesGroup(self, longitudes, latitudes):
item = MapGraphicsLinesGroupItem(longitudes, latitudes)
self.addItem(item)
diff --git a/pytilemap/maptilesources/maptilesource.py b/pytilemap/maptilesources/maptilesource.py
index 92b3db9..87d6962 100644
--- a/pytilemap/maptilesources/maptilesource.py
+++ b/pytilemap/maptilesources/maptilesource.py
@@ -12,7 +12,7 @@ class MapTileSource(QObject):
_minZoom = None
_maxZoom = None
- def __init__(self, tileSize=256, minZoom=2, maxZoom=18, parent=None):
+ def __init__(self, tileSize=256, minZoom=2, maxZoom=17, parent=None):
QObject.__init__(self, parent=parent)
self._tileSize = tileSize
self._minZoom = minZoom
diff --git a/pytilemap/maptilesources/maptilesourcehere.py b/pytilemap/maptilesources/maptilesourcehere.py
index 62c5ffa..3e19d35 100644
--- a/pytilemap/maptilesources/maptilesourcehere.py
+++ b/pytilemap/maptilesources/maptilesourcehere.py
@@ -10,28 +10,34 @@ class MapTileSourceHereDemo(MapTileSourceHTTP):
def __init__(self, tileSize=256, parent=None):
MapTileSourceHTTP.__init__(self, tileSize=tileSize, minZoom=2, maxZoom=20, parent=parent)
assert tileSize == 256 or tileSize == 512
- self._server = 0
+ self._server = 1
+ self._cache_dir = 'api.here.com'
def url(self, x, y, zoom):
self._server += 1
if self._server > 4:
- self._server = 0
- url = "http://%d.base.maps.cit.api.here.com/maptile/2.1/maptile/" % self._server
+ self._server = 1
+ #https://2.aerial.maps.cit.api.here.com/maptile/2.1/maptile/newest/hybrid.day/5/8/13/256/png8
+ url = "http://%d.aerial.maps.cit.api.here.com/maptile/2.1/maptile/" % self._server
+ #url = "http://%d.base.maps.cit.api.here.com/maptile/2.1/maptile/" % self._server
url += "newest/normal.day/%d/%d/%d/%d/png8" % (zoom, x, y, self._tileSize)
url += '?app_id=DemoAppId01082013GAL&app_code=AJKnXv84fjrb0KIHawS0Tg'
+ print (url)
return url
class MapTileSourceHere(MapTileSourceHTTP):
def __init__(self, tileSize=256, app_id='DemoAppId01082013GAL', app_code='AJKnXv84fjrb0KIHawS0Tg',
+ #scheme='hybrid.day', cit=True, tileType='maptile', mapType='aerial', imageFmt='png8',
scheme='normal.day', cit=True, tileType='maptile', mapType='base', imageFmt='png8',
userAgent='(PyQt) TileMap 1.0 - HERE', mapHttpLoader=None,
minZoom=2, maxZoom=20, parent=None):
MapTileSourceHTTP.__init__(self, tileSize=tileSize, minZoom=minZoom, maxZoom=maxZoom,
mapHttpLoader=mapHttpLoader, parent=parent)
+ self._cache_dir = 'api.here.com'
assert tileSize == 256 or tileSize == 512
- self._server = 0
+ self._server = 1
self._app_id = app_id
self._app_code = app_code
@@ -64,7 +70,7 @@ def setOptions(self, scheme=None, tileType=None, mapType=None):
def url(self, x, y, zoom):
self._server += 1
if self._server > 4:
- self._server = 0
+ self._server = 1
args = (self._server, zoom, x, y)
url = self._baseurl % args
diff --git a/pytilemap/maptilesources/maptilesourcehttp.py b/pytilemap/maptilesources/maptilesourcehttp.py
index 4cb47ca..d4f8301 100644
--- a/pytilemap/maptilesources/maptilesourcehttp.py
+++ b/pytilemap/maptilesources/maptilesourcehttp.py
@@ -8,7 +8,10 @@
from .maptilesource import MapTileSource
from ..qtsupport import getQVariantValue, getCacheFolder
-DEFAULT_CACHE_SIZE = 1024 * 1024 * 100
+import os
+from urllib.parse import urlparse
+
+DEFAULT_CACHE_SIZE = 1024 * 1024 * 16000
class MapTileHTTPLoader(QObject):
@@ -31,35 +34,46 @@ def __init__(self, cacheSize=DEFAULT_CACHE_SIZE, userAgent='(PyQt) TileMap 1.0',
self._userAgent = userAgent
self._tileInDownload = dict()
- @Slot(int, int, int, str)
- def loadTile(self, x, y, zoom, url):
+ @Slot(int, int, int, str, str)
+ def loadTile(self, x, y, zoom, url, cache_dir):
if self._manager is None:
self._manager = QNetworkAccessManager(parent=self)
self._manager.finished.connect(self.handleNetworkData)
- cache = QNetworkDiskCache()
- cacheDir = getCacheFolder()
- cache.setCacheDirectory(cacheDir)
- cache.setMaximumCacheSize(self._cacheSize)
- self._manager.setCache(cache)
+ self._cache = './tiles'
key = (x, y, zoom)
url = QUrl(url)
+ #base = parsed_url.netloc
if key not in self._tileInDownload:
- # Request the image to the map service
- request = QNetworkRequest(url=url)
- request.setRawHeader(b'User-Agent', self._userAgent)
- request.setAttribute(QNetworkRequest.User, key)
- request.setAttribute(QNetworkRequest.CacheLoadControlAttribute, QNetworkRequest.PreferCache)
- self._tileInDownload[key] = self._manager.get(request)
+ path = os.path.join(self._cache, str(cache_dir), str(zoom), str(x), str(y)+'.png')
+ if os.path.exists(path):
+ self.tileLoaded.emit(x, y, zoom, open(path, 'rb').read())
+ else:
+ # Request the image to the map service
+ request = QNetworkRequest(url=url)
+ request.setRawHeader(b'User-Agent', self._userAgent)
+ request.setAttribute(QNetworkRequest.User, [key, cache_dir])
+ request.setAttribute(QNetworkRequest.CacheLoadControlAttribute, QNetworkRequest.PreferCache)
+ self._tileInDownload[key] = self._manager.get(request)
+
@Slot(QNetworkReply)
def handleNetworkData(self, reply):
- tp = getQVariantValue(reply.request().attribute(QNetworkRequest.User))
+ [tp, cache_dir] = getQVariantValue(reply.request().attribute(QNetworkRequest.User))
if tp in self._tileInDownload:
del self._tileInDownload[tp]
+ zoom = tp[2]; x = tp[0]; y = tp[1]
+
+ base = cache_dir
+
if not reply.error():
data = reply.readAll()
+ if not os.path.isdir(os.path.join(self._cache, base, str(zoom), str(x))):
+ os.makedirs(os.path.join(self._cache, base, str(zoom), str(x)))
+ fout = open(os.path.join(self._cache, base, str(zoom), str(x), str(y)+'.png'), 'wb')
+ fout.write(data)
+ fout.close()
self.tileLoaded.emit(tp[0], tp[1], tp[2], data)
reply.close()
reply.deleteLater()
@@ -91,6 +105,7 @@ def __init__(self, cacheSize=DEFAULT_CACHE_SIZE, userAgent='(PyQt) TileMap 1.0',
self._loader = MapTileHTTPLoader(cacheSize=cacheSize, userAgent=userAgent)
self._loader.tileLoaded.connect(self.handleTileDataLoaded)
+ self._cache_dir = 'cache'
@Slot()
def close(self):
@@ -101,7 +116,7 @@ def url(self, x, y, zoom):
def requestTile(self, x, y, zoom):
url = self.url(x, y, zoom)
- self._loader.loadTile(x, y, zoom, url)
+ self._loader.loadTile(x, y, zoom, url, self._cache_dir)
@Slot(int, int, int, QByteArray)
def handleTileDataLoaded(self, x, y, zoom, data):
diff --git a/pytilemap/maptilesources/maptilesourceosm.py b/pytilemap/maptilesources/maptilesourceosm.py
index 72d6c4a..bdf9efa 100644
--- a/pytilemap/maptilesources/maptilesourceosm.py
+++ b/pytilemap/maptilesources/maptilesourceosm.py
@@ -7,6 +7,7 @@ class MapTileSourceOSM(MapTileSourceHTTP):
def __init__(self, parent=None):
MapTileSourceHTTP.__init__(self, parent=parent)
+ self._cache_dir = 'tile.openstreetmap.org'
def url(self, x, y, zoom):
url = "http://tile.openstreetmap.org/%d/%d/%d.png" % (zoom, x, y)
diff --git a/pytilemap/red_pin.png b/pytilemap/red_pin.png
new file mode 100644
index 0000000..e4ef1c1
Binary files /dev/null and b/pytilemap/red_pin.png differ
diff --git a/pytilemap/zoom_in_symbol.png b/pytilemap/zoom_in_symbol.png
new file mode 100644
index 0000000..6787135
Binary files /dev/null and b/pytilemap/zoom_in_symbol.png differ
diff --git a/pytilemap/zoom_out_symbol.png b/pytilemap/zoom_out_symbol.png
new file mode 100644
index 0000000..c49ba6a
Binary files /dev/null and b/pytilemap/zoom_out_symbol.png differ
diff --git a/screenshot.png b/screenshot.png
new file mode 100644
index 0000000..b45c566
Binary files /dev/null and b/screenshot.png differ
diff --git a/setup.py b/setup.py
index 2f236e7..5f1bc61 100644
--- a/setup.py
+++ b/setup.py
@@ -19,6 +19,9 @@
description='Widget for tile maps in PyQt',
long_description=long_description,
+ # Required for image files
+ include_package_data=True,
+
# The project's main homepage.
url='https://github.com/allebacco/PyTileMap',