Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom tiling support #75

Merged
merged 10 commits into from Sep 8, 2016
Binary file added .DS_Store
Binary file not shown.
Binary file added src/.DS_Store
Binary file not shown.
13 changes: 12 additions & 1 deletion src/config_reader_helper.py
Expand Up @@ -34,4 +34,15 @@ def try_read_config_bool(parser, section, param, reraise=False):
raise
else:
val = None
return val
return val

@staticmethod
def try_read_config_float(parser, section, param, reraise=False):
try:
val = parser.getfloat(section, param)
except:
if reraise:
raise
else:
val = None
return val
4 changes: 4 additions & 0 deletions src/data_source_info.py
Expand Up @@ -53,6 +53,10 @@ def __init__(self):
self.tms_epsg_crs_id = None
self.tms_postgis_crs_id = None
self.tms_custom_proj = None
self.tms_custom_tile_ranges = None
self.tms_custom_tsize1 = None
self.tms_custom_origin_x = None
self.tms_custom_origin_y = None

self.wms_url = None
self.wms_params = None
Expand Down
8 changes: 8 additions & 0 deletions src/data_source_serializer.py
Expand Up @@ -47,6 +47,10 @@ def read_from_ini(cls, ini_file_path):
ds.tms_epsg_crs_id = ConfigReaderHelper.try_read_config_int(parser, 'tms', 'epsg_crs_id')
ds.tms_postgis_crs_id = ConfigReaderHelper.try_read_config_int(parser, 'tms', 'postgis_crs_id')
ds.tms_custom_proj = ConfigReaderHelper.try_read_config(parser, 'tms', 'custom_proj')
ds.tms_custom_tile_ranges = ConfigReaderHelper.try_read_config(parser, 'tms', 'custom_tile_ranges')
ds.tms_custom_tsize1 = ConfigReaderHelper.try_read_config_float(parser, 'tms', 'custom_tsize1')
ds.tms_custom_origin_x = ConfigReaderHelper.try_read_config_int(parser, 'tms', 'custom_origin_x')
ds.tms_custom_origin_y = ConfigReaderHelper.try_read_config_int(parser, 'tms', 'custom_origin_y')

#WMS
ds.wms_url = ConfigReaderHelper.try_read_config(parser, 'wms', 'url', reraise=(ds.type == KNOWN_DRIVERS.WMS))
Expand Down Expand Up @@ -112,6 +116,10 @@ def write_to_ini(cls, ds_info, ini_file_path):
config.set('tms', 'postgis_crs_id', ds_info.tms_postgis_crs_id)
if ds_info.tms_custom_proj:
config.set('tms', 'custom_proj', ds_info.tms_custom_proj)
config.set('tms', 'custom_tile_ranges', ds_info.tms_custom_tile_ranges)
config.set('tms', 'custom_tsize1', ds_info.tms_custom_tsize1)
config.set('tms', 'custom_origin_x', ds_info.tms_custom_origin_x)
config.set('tms', 'custom_origin_y', ds_info.tms_custom_origin_y)

if ds_info.type == KNOWN_DRIVERS.WMS:
config.set('wms', 'url', ds_info.wms_url)
Expand Down
24 changes: 16 additions & 8 deletions src/py_tiled_layer/tilelayer.py
Expand Up @@ -113,7 +113,7 @@ def __init__(self, plugin, layerDef, creditVisibility=1):
if layerDef.bbox:
self.setExtent(BoundingBox.degreesToMercatorMeters(layerDef.bbox).toQgsRectangle())
else:
self.setExtent(QgsRectangle(-layerDef.TSIZE1, -layerDef.TSIZE1, layerDef.TSIZE1, layerDef.TSIZE1))
self.setExtent(QgsRectangle(-layerDef.tsize1, -layerDef.tsize1, layerDef.tsize1, layerDef.tsize1))
self.setValid(True)
self.tiles = None
self.useLastZoomForPrint = False
Expand Down Expand Up @@ -186,7 +186,7 @@ def draw(self, renderContext):
isDpiEqualToCanvas = painter.device().logicalDpiX() == mapSettings.outputDpi()
if isDpiEqualToCanvas or not self.useLastZoomForPrint:
# calculate zoom level
tile_mpp1 = self.layerDef.TSIZE1 / self.layerDef.TILE_SIZE
tile_mpp1 = self.layerDef.tsize1 / self.layerDef.TILE_SIZE / self.layerDef.xTilesAtZmin() # should be attribute, not method call..
viewport_mpp = extent.width() / painter.viewport().width()
lg = math.log(float(tile_mpp1) / float(viewport_mpp), 2)
zoom = int(math.modf(lg)[1]) + 1*(math.modf(lg)[0] > self.CHANGE_SCALE_VALUE) + 1
Expand All @@ -206,12 +206,20 @@ def draw(self, renderContext):

while True:
# calculate tile range (yOrigin is top)
size = self.layerDef.TSIZE1 / 2 ** (zoom - 1)
matrixSize = 2 ** zoom
ulx = max(0, int((extent.xMinimum() + self.layerDef.TSIZE1) / size))
uly = max(0, int((self.layerDef.TSIZE1 - extent.yMaximum()) / size))
lrx = min(int((extent.xMaximum() + self.layerDef.TSIZE1) / size), matrixSize - 1)
lry = min(int((self.layerDef.TSIZE1 - extent.yMinimum()) / size), matrixSize - 1)
if self.layerDef.custom_tile_ranges is None: # should add xOffset & yOffset in first part of conditional
size = self.layerDef.tsize1 / 2 ** (zoom - 1)
matrixSize = 2 ** zoom
ulx = max(0, int((extent.xMinimum() + self.layerDef.tsize1) / size))
uly = max(0, int((self.layerDef.tsize1 - extent.yMaximum()) / size))
lrx = min(int((extent.xMaximum() + self.layerDef.tsize1) / size), matrixSize - 1)
lry = min(int((self.layerDef.tsize1 - extent.yMinimum()) / size), matrixSize - 1)
else: # for custom_tile_ranges
size = self.layerDef.tsize1 / 2 ** zoom
xmin, xmax, ymin, ymax = self.layerDef.custom_tile_ranges[zoom]
ulx = max(int((extent.xMinimum() - self.layerDef.originX)/ size), xmin)
uly = max(int((self.layerDef.originY - extent.yMaximum())/ size), ymin)
lrx = min(int((extent.xMaximum() - self.layerDef.originX)/ size), xmax)
lry = min(int((self.layerDef.originY - extent.yMinimum())/ size), ymax)

# bounding box limit
if self.layerDef.bbox:
Expand Down
46 changes: 33 additions & 13 deletions src/py_tiled_layer/tiles.py
Expand Up @@ -84,7 +84,7 @@ def __init__(self, zoom, xmin, ymin, xmax, ymax, serviceInfo):
self.xmax = xmax
self.ymax = ymax
self.TILE_SIZE = serviceInfo.TILE_SIZE
self.TSIZE1 = serviceInfo.TSIZE1
self.tsize1 = serviceInfo.tsize1
self.yOriginTop = serviceInfo.yOriginTop
self.serviceInfo = serviceInfo
self.tiles = {}
Expand Down Expand Up @@ -115,17 +115,24 @@ def image(self):
return image

def extent(self):
size = self.TSIZE1 / 2 ** (self.zoom - 1)
return QgsRectangle(self.xmin * size - self.TSIZE1, self.TSIZE1 - (self.ymax + 1) * size,
(self.xmax + 1) * size - self.TSIZE1, self.TSIZE1 - self.ymin * size)

if self.serviceInfo.custom_tile_ranges is None:
size = self.tsize1 / 2 ** (self.zoom - 1)
return QgsRectangle(self.xmin * size - self.tsize1, self.tsize1 - (self.ymax + 1) * size,
(self.xmax + 1) * size - self.tsize1, self.tsize1 - self.ymin * size)
else:
originX = self.serviceInfo.originX
originY = self.serviceInfo.originY
size = self.tsize1 / 2 ** self.zoom
return QgsRectangle(originX + self.xmin * size, originY - (self.ymax + 1) * size,
originX + (self.xmax + 1) * size, originY - self.ymin * size)

class TileServiceInfo:
TILE_SIZE = 256
TSIZE1 = 20037508.342789244
# TSIZE1 = 20037508.342789244 # (R * math.pi)

def __init__(self, title, credit, serviceUrl, yOriginTop=1, zmin=TileDefaultSettings.ZMIN,
zmax=TileDefaultSettings.ZMAX, bbox=None, epsg_crs_id=None, postgis_crs_id=None, custom_proj=None):
zmax=TileDefaultSettings.ZMAX, bbox=None, epsg_crs_id=None, postgis_crs_id=None,
custom_proj=None, custom_tile_ranges=None, tsize1=R * math.pi, originX= -R * math.pi, originY = R * math.pi):
self.title = title
self.credit = credit
self.serviceUrl = serviceUrl
Expand All @@ -136,6 +143,19 @@ def __init__(self, title, credit, serviceUrl, yOriginTop=1, zmin=TileDefaultSett
self.epsg_crs_id = epsg_crs_id
self.postgis_crs_id = postgis_crs_id
self.custom_proj = custom_proj
self.tsize1 = tsize1
self.custom_tile_ranges = custom_tile_ranges
self.originX = originX
self.originY = originY

def xTilesAtZmin(self):
tranges = self.custom_tile_ranges
if tranges is None:
return 1 # non-custom tile scheme has 1 tile at zmin
else:
zmin = sorted(list(tranges.keys()))[0]
xmax, xmin = tranges[zmin][-1], tranges[zmin][-2]
return xmax - xmin + 1

def tileUrl(self, zoom, x, y):
if not self.yOriginTop:
Expand All @@ -160,15 +180,15 @@ def tileUrl(self, zoom, x, y):
return self.serviceUrl.replace("{z}", str(zoom)).replace("{x}", str(x)).replace("{y}", str(y))

def getTileRect(self, zoom, x, y):
size = self.TSIZE1 / 2 ** (zoom - 1)
return QgsRectangle(x * size - self.TSIZE1, self.TSIZE1 - y * size, (x + 1) * size - self.TSIZE1,
self.TSIZE1 - (y + 1) * size)
size = self.tsize1 / 2 ** (zoom - 1)
return QgsRectangle(x * size - self.tsize1, self.tsize1 - y * size, (x + 1) * size - self.tsize1,
self.tsize1 - (y + 1) * size)

def degreesToTile(self, zoom, lon, lat):
x, y = degreesToMercatorMeters(lon, lat)
size = self.TSIZE1 / 2 ** (zoom - 1)
tx = int((x + self.TSIZE1) / size)
ty = int((self.TSIZE1 - y) / size)
size = self.tsize1 / 2 ** (zoom - 1)
tx = int((x + self.tsize1) / size)
ty = int((self.tsize1 - y) / size)
return tx, ty

def bboxDegreesToTileRange(self, zoom, bbox):
Expand Down
13 changes: 11 additions & 2 deletions src/quick_map_services.py
Expand Up @@ -23,6 +23,7 @@
import os.path
import urlparse
import xml.etree.ElementTree as ET
import ast

from PyQt4.QtCore import QSettings, QTranslator, qVersion, QCoreApplication, Qt
from PyQt4.QtGui import QAction, QIcon, QToolButton, QMenu, QMessageBox, QDialog
Expand Down Expand Up @@ -114,7 +115,7 @@ def initGui(self):
icon_path = self.plugin_dir + '/icons/mActionAddLayer.png'
self.menu = QMenu(self.tr(u'QuickMapServices'))
self.menu.setIcon(QIcon(icon_path))

self.build_menu_tree()

# add to QGIS menu/toolbars
Expand Down Expand Up @@ -179,6 +180,14 @@ def insert_layer(self):
service_info.epsg_crs_id = ds.tms_epsg_crs_id
service_info.postgis_crs_id = ds.tms_postgis_crs_id
service_info.custom_proj = ds.tms_custom_proj
if ds.tms_custom_tile_ranges is not None: # needs try block & checks that keys are integers etc..
service_info.custom_tile_ranges = ast.literal_eval(ds.tms_custom_tile_ranges)
if ds.tms_custom_tsize1 is not None:
service_info.tsize1 = ds.tms_custom_tsize1
if ds.tms_custom_origin_x is not None:
service_info.originX = ds.tms_custom_origin_x
if ds.tms_custom_origin_y is not None:
service_info.originY = ds.tms_custom_origin_y
layer = TileLayer(self, service_info, False)
layers4add.append(layer)
if ds.type == KNOWN_DRIVERS.GDAL:
Expand Down Expand Up @@ -268,7 +277,7 @@ def build_menu_tree(self):
data_sources.sort(key=lambda x: x.alias or x.id)

ds_hide_list = PluginSettings.get_hide_ds_id_list()

for ds in data_sources:
if ds.id in ds_hide_list:
continue
Expand Down