Skip to content

Instantly share code, notes, and snippets.

@riccardoklinger
Last active August 28, 2019 13:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save riccardoklinger/e3e2c512a366c8a6a296c10eef9c4162 to your computer and use it in GitHub Desktop.
Save riccardoklinger/e3e2c512a366c8a6a296c10eef9c4162 to your computer and use it in GitHub Desktop.
new Labels OL
from . import xmltodict
from qgis2web.olStyleScripts import getStrokeStyle
def measureControlScript():
measureControl = """
var measuring = false;
measureControl = function(opt_options) {
var options = opt_options || {};
var button = document.createElement('button');
button.className += ' fas fa-ruler ';
var this_ = this;
var handleMeasure = function(e) {
if (!measuring) {
this_.getMap().addInteraction(draw);
createHelpTooltip();
createMeasureTooltip();
measuring = true;
} else {
this_.getMap().removeInteraction(draw);
measuring = false;
this_.getMap().removeOverlay(helpTooltip);
this_.getMap().removeOverlay(measureTooltip);
}
};
button.addEventListener('click', handleMeasure, false);
button.addEventListener('touchstart', handleMeasure, false);
var element = document.createElement('div');
element.className = 'measure-control ol-unselectable ol-control';
element.appendChild(button);
ol.control.Control.call(this, {
element: element,
target: options.target
});
};
ol.inherits(measureControl, ol.control.Control);"""
return measureControl
def measuringScript():
measuring = """
map.on('pointermove', function(evt) {
if (evt.dragging) {
return;
}
if (measuring) {
/** @type {string} */
var helpMsg = 'Click to start drawing';
if (sketch) {
var geom = (sketch.getGeometry());
if (geom instanceof ol.geom.Polygon) {
helpMsg = continuePolygonMsg;
} else if (geom instanceof ol.geom.LineString) {
helpMsg = continueLineMsg;
}
}
helpTooltipElement.innerHTML = helpMsg;
helpTooltip.setPosition(evt.coordinate);
}
});
"""
return measuring
def measureScript():
measure = """
/**
* Currently drawn feature.
* @type {ol.Feature}
*/
/**
* The help tooltip element.
* @type {Element}
*/
var helpTooltipElement;
/**
* Overlay to show the help messages.
* @type {ol.Overlay}
*/
var helpTooltip;
/**
* The measure tooltip element.
* @type {Element}
*/
var measureTooltipElement;
/**
* Overlay to show the measurement.
* @type {ol.Overlay}
*/
var measureTooltip;
/**
* Message to show when the user is drawing a line.
* @type {string}
*/
var continueLineMsg = 'Click to continue drawing the line';
var source = new ol.source.Vector();
var measureLayer = new ol.layer.Vector({
source: source,
style: new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(255, 255, 255, 0.2)'
}),
stroke: new ol.style.Stroke({
color: '#ffcc33',
width: 3
}),
image: new ol.style.Circle({
radius: 7,
fill: new ol.style.Fill({
color: '#ffcc33'
})
})
})
});
map.addLayer(measureLayer);
var draw; // global so we can remove it later
function addInteraction() {
var type = 'LineString';
draw = new ol.interaction.Draw({
source: source,
type: /** @type {ol.geom.GeometryType} */ (type),
style: new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(255, 255, 255, 0.2)'
}),
stroke: new ol.style.Stroke({
color: 'rgba(0, 0, 0, 0.5)',
lineDash: [10, 10],
width: 2
}),
image: new ol.style.Circle({
radius: 5,
stroke: new ol.style.Stroke({
color: 'rgba(0, 0, 0, 0.7)'
}),
fill: new ol.style.Fill({
color: 'rgba(255, 255, 255, 0.2)'
})
})
})
});
var listener;
draw.on('drawstart',
function(evt) {
// set sketch
sketch = evt.feature;
/** @type {ol.Coordinate|undefined} */
var tooltipCoord = evt.coordinate;
listener = sketch.getGeometry().on('change', function(evt) {
var geom = evt.target;
var output;
output = formatLength( /** @type {ol.geom.LineString} */ (geom));
tooltipCoord = geom.getLastCoordinate();
measureTooltipElement.innerHTML = output;
measureTooltip.setPosition(tooltipCoord);
});
}, this);
draw.on('drawend',
function(evt) {
measureTooltipElement.className = 'tooltip tooltip-static';
measureTooltip.setOffset([0, -7]);
// unset sketch
sketch = null;
// unset tooltip so that a new one can be created
measureTooltipElement = null;
createMeasureTooltip();
ol.Observable.unByKey(listener);
}, this);
}
/**
* Creates a new help tooltip
*/
function createHelpTooltip() {
if (helpTooltipElement) {
helpTooltipElement.parentNode.removeChild(helpTooltipElement);
}
helpTooltipElement = document.createElement('div');
helpTooltipElement.className = 'tooltip hidden';
helpTooltip = new ol.Overlay({
element: helpTooltipElement,
offset: [15, 0],
positioning: 'center-left'
});
map.addOverlay(helpTooltip);
}
/**
* Creates a new measure tooltip
*/
function createMeasureTooltip() {
if (measureTooltipElement) {
measureTooltipElement.parentNode.removeChild(measureTooltipElement);
}
measureTooltipElement = document.createElement('div');
measureTooltipElement.className = 'tooltip tooltip-measure';
measureTooltip = new ol.Overlay({
element: measureTooltipElement,
offset: [0, -15],
positioning: 'bottom-center'
});
map.addOverlay(measureTooltip);
}
"""
return measure
def measureUnitFeetScript():
measureUnitFeet = """function convertToFeet(length) {
feet_length = length * 3.2808;
return feet_length
}
/**
* format length output
* @param {ol.geom.LineString} line
* @return {string}
*/
var formatLength = function(line) {
var length;
var coordinates = line.getCoordinates();
length = 0;
var sourceProj = map.getView().getProjection();
for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) {
var c1 = ol.proj.transform(coordinates[i], sourceProj, 'EPSG:4326');
var c2 = ol.proj.transform(coordinates[i + 1], sourceProj, 'EPSG:4326');
length += ol.sphere.getDistance(c1, c2);
}
feet_length = convertToFeet(length)
var output;
if (feet_length > 5280) {
output = (Math.round(feet_length / 5280 * 100) / 100) + ' miles';
} else {
output = (Math.round(feet_length * 100) / 100) + ' ft';
}
return output;
};
addInteraction();
"""
return measureUnitFeet
def measureUnitMetricScript():
measureUnitMetric = """
/**
* format length output
* @param {ol.geom.LineString} line
* @return {string}
*/
var formatLength = function(line) {
var length;
var coordinates = line.getCoordinates();
length = 0;
var sourceProj = map.getView().getProjection();
for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) {
var c1 = ol.proj.transform(coordinates[i], sourceProj, 'EPSG:4326');
var c2 = ol.proj.transform(coordinates[i + 1], sourceProj, 'EPSG:4326');
length += ol.sphere.getDistance(c1, c2);
}
var output;
if (length > 100) {
output = (Math.round(length / 1000 * 100) / 100) +
' ' + 'km';
} else {
output = (Math.round(length * 100) / 100) +
' ' + 'm';
}
return output;
};
addInteraction();
"""
return measureUnitMetric
def measureStyleScript(controlCount):
pos = 65 + (controlCount * 35)
touchPos = 80 + (controlCount * 50)
measureStyle = """
<style>
.tooltip {
position: relative;
background: rgba(0, 0, 0, 0.5);
border-radius: 4px;
color: white;
padding: 4px 8px;
opacity: 0.7;
white-space: nowrap;
}
.tooltip-measure {
opacity: 1;
font-weight: bold;
}
.tooltip-static {
background-color: #ffcc33;
color: black;
border: 1px solid white;
}
.tooltip-measure:before,
.tooltip-static:before {
border-top: 6px solid rgba(0, 0, 0, 0.5);
border-right: 6px solid transparent;
border-left: 6px solid transparent;
content: "";
position: absolute;
bottom: -6px;
margin-left: -7px;
left: 50%%;
}
.tooltip-static:before {
border-top-color: #ffcc33;
}
.measure-control {
top: %(pos)dpx;
left: .5em;
}
.ol-touch .measure-control {
top: %(touchPos)dpx;
}
</style>""" % {"pos": pos, "touchPos": touchPos}
return measureStyle
def layerSearchStyleScript(controlCount):
pos = 65 + (controlCount * 35)
touchPos = 80 + (controlCount * 50)
layerSearchStyle = """
<style>
.search-layer {
top: %(pos)dpx;
left: .5em;
}
.ol-touch .search-layer {
top: %(touchPos)dpx;
}
</style>""" % {"pos": pos, "touchPos": touchPos}
return (layerSearchStyle, controlCount)
def geolocation(geolocate):
if geolocate:
return """
var geolocation = new ol.Geolocation({
projection: map.getView().getProjection()
});
var accuracyFeature = new ol.Feature();
geolocation.on('change:accuracyGeometry', function() {
accuracyFeature.setGeometry(geolocation.getAccuracyGeometry());
});
var positionFeature = new ol.Feature();
positionFeature.setStyle(new ol.style.Style({
image: new ol.style.Circle({
radius: 6,
fill: new ol.style.Fill({
color: '#3399CC'
}),
stroke: new ol.style.Stroke({
color: '#fff',
width: 2
})
})
}));
geolocation.on('change:position', function() {
var coordinates = geolocation.getPosition();
positionFeature.setGeometry(coordinates ?
new ol.geom.Point(coordinates) : null);
});
var geolocateOverlay = new ol.layer.Vector({
source: new ol.source.Vector({
features: [accuracyFeature, positionFeature]
})
});
geolocation.setTracking(true);
"""
else:
return ""
def geolocationHead(geolocate):
if geolocate:
return """
isTracking = false;
geolocateControl = function(opt_options) {
var options = opt_options || {};
var button = document.createElement('button');
button.className += ' fa fa-map-marker';
var handleGeolocate = function() {
if (isTracking) {
map.removeLayer(geolocateOverlay);
isTracking = false;
} else if (geolocation.getTracking()) {
map.addLayer(geolocateOverlay);
map.getView().setCenter(geolocation.getPosition());
isTracking = true;
}
};
button.addEventListener('click', handleGeolocate, false);
button.addEventListener('touchstart', handleGeolocate, false);
var element = document.createElement('div');
element.className = 'geolocate ol-unselectable ol-control';
element.appendChild(button);
ol.control.Control.call(this, {
element: element,
target: options.target
});
};
ol.inherits(geolocateControl, ol.control.Control);"""
else:
return ""
def geolocateStyle(geolocate, controlCount):
if geolocate:
ctrlPos = 65 + (controlCount * 35)
touchCtrlPos = 80 + (controlCount * 50)
controlCount = controlCount + 1
return ("""
<style>
.geolocate {
top: %dpx;
left: .5em;
}
.ol-touch .geolocate {
top: %dpx;
}
</style>""" % (ctrlPos, touchCtrlPos), controlCount)
else:
return ("", controlCount)
def geocodeLinks(geocode):
if geocode:
returnVal = """
<link href="resources/ol3-geocoder.min.css" rel="stylesheet">"""
return returnVal
else:
return ""
def geocodeJS(geocode):
if geocode:
returnVal = """
<script src="resources/ol3-geocoder.js"></script>"""
return returnVal
else:
return ""
def geocodeScript(geocode):
if geocode:
return """
var geocoder = new Geocoder('nominatim', {
provider: 'osm',
lang: 'en-US',
placeholder: 'Search for ...',
limit: 5,
keepOpen: true
});
map.addControl(geocoder);
document.getElementsByClassName('gcd-gl-btn')[0].className += ' fa fa-search';
"""
else:
return ""
def getGrid(project):
grid = ""
if project.readBoolEntry("Grid", "/Enabled", False)[0]:
stroke = project.readEntry("Grid", "/LineSymbol", "")[0]
strokeDict = xmltodict.parse(stroke)
symbol = strokeDict["symbol"]
layer = symbol["layer"]
props = layer["prop"]
lineunits = "px"
linecap = 0
linejoin = 0
width = 1
color = "#000000"
dashed = "no"
for prop in props:
if prop["@k"] == "line_color":
color = "'rgba(%s)'" % prop["@v"]
if prop["@k"] == "line_style":
dashed = prop["@v"]
if prop["@k"] == "line_width":
width = prop["@v"]
if prop["@k"] == "capstyle":
linecap = prop["@v"]
if prop["@k"] == "joinstyle":
linejoin = prop["@v"]
strokeStyle, _ = getStrokeStyle(color, dashed, width, lineunits,
linecap, linejoin)
strokeStyle = strokeStyle.replace("stroke:", "strokeStyle:")
grid = """
var gcl = new ol.Graticule({%s});
gcl.setMap(map);""" % strokeStyle
return grid
def getM2px(mapUnitsLayers):
m2px = ""
if len(mapUnitsLayers) > 0:
m2px = """
function m2px(m) {
var centerLatLng = map.getView().getCenter();
var pointC = map.getPixelFromCoordinate(centerLatLng);
var pointX = [pointC[0] + 100, pointC[1]];
var latLngC = map.getCoordinateFromPixel(pointC);
var latLngX = map.getCoordinateFromPixel(pointX);
var lineX = new ol.geom.LineString([latLngC, latLngX]);
var distanceX = lineX.getLength() / 100;
reciprocal = 1 / distanceX;
px = Math.ceil(reciprocal);
return px;
}"""
return m2px
def getMapUnitLayers(mapUnitsLayers):
mapUnitLayers = ""
if len(mapUnitsLayers) > 0:
lyrs = []
for layer in mapUnitsLayers:
lyrs.append("""
lyr_%s.setStyle(style_%s);""" % (layer, layer))
lyrScripts = "".join(lyrs)
mapUnitLayers = """
map.getView().on('change:resolution', function(evt){
%s
});""" % lyrScripts
return mapUnitLayers
def getTitle(projectTitle, abstract):
titleJS = ""
if projectTitle != "":
titleJS += """
window.app = {{}};
var app = window.app;
app.MapTitle = function(opt_options) {{
var options = opt_options || {{}};
var this_ = this;
var mapTitle = document.createElement('div');
mapTitle.className = "ol-title ol-unselectable ol-control";
mapTitle.innerHTML = "{}" ;
ol.control.Control.call(this, {{
element: mapTitle,
target: options.target
}});
}};
ol.inherits(app.MapTitle, ol.control.Control);
""".format(projectTitle)
if abstract != "":
titleJS += ""
return titleJS
# qgis-ol3 Creates OpenLayers map from QGIS layers
# Copyright (C) 2014 Victor Olaya (volayaf@gmail.com)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import os
import re
from datetime import datetime
from qgis.core import (QgsProject,
QgsCoordinateReferenceSystem,
QgsCoordinateTransform,
QgsRectangle,
QgsCsException)
from qgis.PyQt.QtCore import Qt, QObject
from qgis.PyQt.QtGui import QCursor
from qgis.PyQt.QtWidgets import QApplication
from qgis2web.utils import exportLayers, replaceInTemplate
from qgis2web.olFileScripts import (writeFiles,
writeHTMLstart,
writeLayerSearch,
writeScriptIncludes)
from qgis2web.olLayerScripts import writeLayersAndGroups
from qgis2web.olScriptStrings import (measureScript,
measuringScript,
measureControlScript,
measureUnitMetricScript,
measureUnitFeetScript,
measureStyleScript,
layerSearchStyleScript,
geolocation,
geolocateStyle,
geolocationHead,
geocodeLinks,
geocodeJS,
geocodeScript,
getGrid,
getM2px,
getMapUnitLayers,
getTitle)
from qgis2web.olStyleScripts import exportStyles
from qgis2web.writer import (Writer,
WriterResult,
translator)
from qgis2web.feedbackDialog import Feedback
class OpenLayersWriter(Writer):
"""
Writer for creation of web maps based on the OpenLayers
JavaScript library.
"""
def __init__(self):
super(OpenLayersWriter, self).__init__()
@classmethod
def type(cls):
return 'openlayers'
@classmethod
def name(cls):
return QObject.tr(translator, 'OpenLayers')
def write(self, iface, dest_folder, feedback=None):
if not feedback:
feedback = Feedback()
feedback.showFeedback('Creating OpenLayers map...')
self.preview_file = self.writeOL(iface, feedback,
layers=self.layers,
groups=self.groups,
popup=self.popup,
visible=self.visible,
interactive=self.interactive,
json=self.json,
clustered=self.cluster,
getFeatureInfo=self.getFeatureInfo,
settings=self.params,
folder=dest_folder)
result = WriterResult()
result.index_file = self.preview_file
result.folder = os.path.dirname(self.preview_file)
for dirpath, dirnames, filenames in os.walk(result.folder):
result.files.extend([os.path.join(dirpath, f) for f in filenames])
return result
@classmethod
def writeOL(cls, iface, feedback, layers, groups, popup, visible,
interactive, json, clustered, getFeatureInfo, settings,
folder):
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
mapSettings = iface.mapCanvas().mapSettings()
controlCount = 0
stamp = datetime.now().strftime("%Y_%m_%d-%H_%M_%S_%f")
folder = os.path.join(folder, 'qgis2web_' + stamp)
restrictToExtent = settings["Scale/Zoom"]["Restrict to extent"]
matchCRS = settings["Appearance"]["Match project CRS"]
precision = settings["Data export"]["Precision"]
optimize = settings["Data export"]["Minify GeoJSON files"]
extent = settings["Scale/Zoom"]["Extent"]
mapbounds = bounds(iface, extent == "Canvas extent", layers, matchCRS)
fullextent = bounds(iface, False, layers, matchCRS)
geolocateUser = settings["Appearance"]["Geolocate user"]
maxZoom = int(settings["Scale/Zoom"]["Max zoom level"])
minZoom = int(settings["Scale/Zoom"]["Min zoom level"])
popupsOnHover = settings["Appearance"]["Show popups on hover"]
highlightFeatures = settings["Appearance"]["Highlight on hover"]
geocode = settings["Appearance"]["Add address search"]
measureTool = settings["Appearance"]["Measure tool"]
addLayersList = settings["Appearance"]["Add layers list"]
htmlTemplate = settings["Appearance"]["Template"]
layerSearch = settings["Appearance"]["Layer search"]
searchLayer = settings["Appearance"]["Search layer"]
widgetAccent = settings["Appearance"]["Widget Icon"]
widgetBackground = settings["Appearance"]["Widget Background"]
writeFiles(folder, restrictToExtent, feedback)
exportLayers(iface, layers, folder, precision, optimize,
popup, json, restrictToExtent, extent, feedback, matchCRS)
mapUnitsLayers = exportStyles(layers, folder, clustered, feedback)
mapUnitLayers = getMapUnitLayers(mapUnitsLayers)
osmb = writeLayersAndGroups(layers, groups, visible, interactive,
folder, popup, settings, json, matchCRS,
clustered, getFeatureInfo, iface,
restrictToExtent, extent, mapbounds,
mapSettings.destinationCrs().authid())
(jsAddress,
cssAddress, controlCount) = writeHTMLstart(settings, controlCount,
osmb, feedback)
(geojsonVars, wfsVars, styleVars) = writeScriptIncludes(layers,
json, matchCRS)
popupLayers = "popupLayers = [%s];" % ",".join(
['1' for field in popup])
project = QgsProject.instance()
pageTitle = project.title()
abstract = project.metadata().abstract()
mapTitles = getTitle(pageTitle, abstract)
controls = getControls(project, measureTool, geolocateUser, mapTitles)
layersList = getLayersList(addLayersList)
backgroundColor = getBackground(mapSettings, widgetAccent,
widgetBackground)
(geolocateCode, controlCount) = geolocateStyle(geolocateUser,
controlCount)
backgroundColor += geolocateCode
mapextent = "extent: %s," % mapbounds if restrictToExtent else ""
onHover = str(popupsOnHover).lower()
highlight = str(highlightFeatures).lower()
highlightFill = mapSettings.selectionColor().name()
(proj, proj4, view) = getCRSView(mapextent, fullextent, maxZoom,
minZoom, matchCRS, mapSettings)
(measureControl, measuring, measure, measureUnit, measureStyle,
controlCount) = getMeasure(measureTool, controlCount)
geolocateHead = geolocationHead(geolocateUser)
geolocate = geolocation(geolocateUser)
geocodingLinks = geocodeLinks(geocode)
geocodingJS = geocodeJS(geocode)
geocodingScript = geocodeScript(geocode)
m2px = getM2px(mapUnitsLayers)
(extracss, controlCount) = getCSS(geocode, geolocateUser, layerSearch,
controlCount, mapTitles)
(jsAddress, cssAddress,
layerSearch, controlCount) = writeLayerSearch(cssAddress, jsAddress,
controlCount,
layerSearch,
searchLayer, feedback)
ol3layerswitcher = getLayerSwitcher()
ol3popup = getPopup()
ol3qgis2webjs = getJS(osmb)
ol3layers = getLayers()
mapSize = iface.mapCanvas().size()
exp_js = getExpJS()
grid = getGrid(project)
values = {"@PAGETITLE@": pageTitle,
"@CSSADDRESS@": cssAddress,
"@EXTRACSS@": extracss,
"@JSADDRESS@": jsAddress,
"@MAP_WIDTH@": str(mapSize.width()) + "px",
"@MAP_HEIGHT@": str(mapSize.height()) + "px",
"@OL3_STYLEVARS@": styleVars,
"@OL3_BACKGROUNDCOLOR@": backgroundColor,
"@OL3_POPUP@": ol3popup,
"@OL3_GEOJSONVARS@": geojsonVars,
"@OL3_WFSVARS@": wfsVars,
"@OL3_PROJ4@": proj4,
"@OL3_PROJDEF@": proj,
"@OL3_GEOCODINGLINKS@": geocodingLinks,
"@OL3_GEOCODINGJS@": geocodingJS,
"@QGIS2WEBJS@": ol3qgis2webjs,
"@OL3_LAYERSWITCHER@": ol3layerswitcher,
"@OL3_LAYERS@": ol3layers,
"@OL3_MEASURESTYLE@": measureStyle,
"@EXP_JS@": exp_js,
"@LEAFLET_ADDRESSCSS@": "",
"@LEAFLET_MEASURECSS@": "",
"@LEAFLET_LAYERFILTERCSS@": "",
"@LEAFLET_EXTRAJS@": "",
"@LEAFLET_ADDRESSJS@": "",
"@LEAFLET_MEASUREJS@": "",
"@LEAFLET_CRSJS@": "",
"@LEAFLET_LAYERSEARCHCSS@": "",
"@LEAFLET_LAYERSEARCHJS@": "",
"@LEAFLET_LAYERFILTERJS@": "",
"@LEAFLET_CLUSTERCSS@": "",
"@LEAFLET_CLUSTERJS@": ""}
with open(os.path.join(folder, "index.html"), "w") as f:
htmlTemplate = htmlTemplate
if htmlTemplate == "":
htmlTemplate = "full-screen"
templateOutput = replaceInTemplate(
htmlTemplate + ".html", values)
templateOutput = re.sub(r'\n[\s_]+\n', '\n', templateOutput)
f.write(templateOutput)
values = {"@GEOLOCATEHEAD@": geolocateHead,
"@MapTitle@": mapTitles,
"@BOUNDS@": mapbounds,
"@CONTROLS@": ",".join(controls),
"@LAYERSLIST@": layersList,
"@POPUPLAYERS@": popupLayers,
"@VIEW@": view,
"@LAYERSEARCH@": layerSearch,
"@ONHOVER@": onHover,
"@DOHIGHLIGHT@": highlight,
"@HIGHLIGHTFILL@": highlightFill,
"@GEOLOCATE@": geolocate,
"@GEOCODINGSCRIPT@": geocodingScript,
"@MEASURECONTROL@": measureControl,
"@MEASURING@": measuring,
"@MEASURE@": measure,
"@MEASUREUNIT@": measureUnit,
"@GRID@": grid,
"@M2PX@": m2px,
"@MAPUNITLAYERS@": mapUnitLayers}
with open(os.path.join(folder, "resources", "qgis2web.js"),
"w") as f:
out = replaceInScript("qgis2web.js", values)
f.write(out)
QApplication.restoreOverrideCursor()
return os.path.join(folder, "index.html")
def replaceInScript(template, values):
path = os.path.join(os.path.dirname(__file__), "resources", template)
with open(path) as f:
lines = f.readlines()
s = "".join(lines)
for name, value in values.items():
s = s.replace(name, value)
return s
def bounds(iface, useCanvas, layers, matchCRS):
if useCanvas:
canvas = iface.mapCanvas()
canvasCrs = canvas.mapSettings().destinationCrs()
if not matchCRS:
epsg3857 = QgsCoordinateReferenceSystem("EPSG:3857")
try:
transform = QgsCoordinateTransform(canvasCrs, epsg3857,
QgsProject.instance())
except Exception:
transform = QgsCoordinateTransform(canvasCrs, epsg3857)
try:
extent = transform.transformBoundingBox(canvas.extent())
except QgsCsException:
extent = QgsRectangle(-20026376.39, -20048966.10,
20026376.39, 20048966.10)
else:
extent = canvas.extent()
else:
extent = None
for layer in layers:
if not matchCRS:
epsg3857 = QgsCoordinateReferenceSystem("EPSG:3857")
try:
transform = QgsCoordinateTransform(layer.crs(), epsg3857,
QgsProject.instance())
except Exception:
transform = QgsCoordinateTransform(layer.crs(), epsg3857)
try:
layerExtent = transform.transformBoundingBox(
layer.extent())
except QgsCsException:
layerExtent = QgsRectangle(-20026376.39, -20048966.10,
20026376.39, 20048966.10)
else:
layerExtent = layer.extent()
if extent is None:
extent = layerExtent
else:
extent.combineExtentWith(layerExtent)
if extent is None:
extent = QgsRectangle(-20026376.39, -20048966.10,
20026376.39, 20048966.10)
return "[%f, %f, %f, %f]" % (extent.xMinimum(), extent.yMinimum(),
extent.xMaximum(), extent.yMaximum())
def getControls(project, measureTool, geolocateUser, mapTitles):
controls = ['expandedAttribution']
if project.readBoolEntry("ScaleBar", "/Enabled", False)[0]:
controls.append("new ol.control.ScaleLine({})")
if measureTool != "None":
controls.append('new measureControl()')
if geolocateUser:
controls.append('new geolocateControl()')
if mapTitles != "":
controls.append('new app.MapTitle()')
return controls
def getLayersList(addLayersList):
if (addLayersList and addLayersList != "" and addLayersList != "None"):
layersList = """
var layerSwitcher = new ol.control.LayerSwitcher({tipLabel: "Layers"});
map.addControl(layerSwitcher);"""
if addLayersList == "Expanded":
layersList += """
layerSwitcher.hidePanel = function() {};
layerSwitcher.showPanel();
"""
else:
layersList = ""
return layersList
def getBackground(mapSettings, widgetAccent, widgetBackground):
return """
<style>
html, body {{
background-color: {bgcol};
}}
.ol-control button {{
background-color: {widgetBackground} !important;
color: {widgetAccent} !important;
border-radius: 0px !important;
}}
.ol-zoom, .geolocate, .gcd-gl-control .ol-control {{
background-color: rgba(255,255,255,.4) !important;
padding: 3px !important;
}}
.ol-scale-line {{
background: none !important;
}}
.ol-scale-line-inner {{
border: 2px solid {widgetBackground} !important;
border-top: none !important;
background: rgba(255, 255, 255, 0.5) !important;
color: black !important;
}}
</style>
""".format(bgcol=mapSettings.backgroundColor().name(),
widgetBackground=widgetBackground,
widgetAccent=widgetAccent)
def getCRSView(mapextent, fullextent, maxZoom, minZoom, matchCRS, mapSettings):
units = ['m', 'ft', 'degrees', '']
proj4 = ""
proj = ""
view = "%s maxZoom: %d, minZoom: %d" % (mapextent, maxZoom, minZoom)
if matchCRS:
proj4 = """
<script src="resources/proj4.js">"""
proj4 += "</script>"
proj = "<script>proj4.defs('{epsg}','{defn}');</script>".format(
epsg=mapSettings.destinationCrs().authid(),
defn=mapSettings.destinationCrs().toProj4())
unit = mapSettings.destinationCrs().mapUnits()
view += """, projection: new ol.proj.Projection({
code: '%s',
extent: %s,
units: '%s'})""" % (mapSettings.destinationCrs().authid(),
fullextent, units[unit])
return (proj, proj4, view)
def getMeasure(measureTool, controlCount):
if measureTool != "None":
measureControl = measureControlScript()
measuring = measuringScript()
measure = measureScript()
if measureTool == "Imperial":
measureUnit = measureUnitFeetScript()
else:
measureUnit = measureUnitMetricScript()
measureStyle = measureStyleScript(controlCount)
controlCount = controlCount + 1
else:
measureControl = ""
measuring = ""
measure = ""
measureUnit = ""
measureStyle = ""
return (measureControl, measuring, measure, measureUnit, measureStyle,
controlCount)
def getCSS(geocode, geolocateUser, layerSearch, controlCount, mapTitles):
extracss = """
<link rel="stylesheet" """
extracss += """href="./resources/ol3-layerswitcher.css">
<link rel="stylesheet" """
extracss += """href="./resources/qgis2web.css">"""
if geocode:
geocodePos = 65 + (controlCount * 35)
touchPos = 80 + (controlCount * 50)
controlCount += 1
extracss += """
<style>
.ol-geocoder.gcd-gl-container {
top: %dpx!important;
left: .5em!important;
width: 2.1em!important;
height: 2.1em!important;
}
.ol-geocoder .gcd-gl-container{
width: 2.1em!important;
height: 2.1em!important;
}
.ol-geocoder .gcd-gl-control{
width: 2.1em!important;
}
.ol-geocoder .gcd-gl-expanded {
width: 15.625em!important;
height: 2.1875em;
}
.ol-touch .ol-geocoder.gcd-gl-container{
top: %dpx!important;
}
.ol-geocoder .gcd-gl-btn {
width: 1.375em!important;
height: 1.375em!important;
top: .225em!important;
background-image: none!important;
}
</style>""" % (geocodePos, touchPos)
if layerSearch:
(layerSearchStyle, controlCount) = layerSearchStyleScript(controlCount)
extracss += layerSearchStyle
if mapTitles != "":
extracss += """
<style>
.ol-title {
font-size: large;
color: #666;
top: .5em;
right: .5em;
}
</style>"""
return (extracss, controlCount)
def getLayerSwitcher():
return """
<script src="./resources/ol3-layerswitcher.js"></script>"""
def getPopup():
return """<div id="popup" class="ol-popup">
<a href="#" id="popup-closer" class="ol-popup-closer"></a>
<div id="popup-content"></div>
</div>"""
def getJS(osmb):
ol3qgis2webjs = """<script src="./resources/qgis2web.js"></script>
<script src="./resources/Autolinker.min.js"></script>"""
if osmb != "":
ol3qgis2webjs += """
<script>{osmb}</script>""".format(osmb=osmb)
return ol3qgis2webjs
def getLayers():
return """
<script src="./layers/layers.js" type="text/javascript"></script>"""
def getExpJS():
return """
<script src="resources/qgis2web_expressions.js"></script>"""
@GEOLOCATEHEAD@
@MEASURECONTROL@
var container = document.getElementById('popup');
var content = document.getElementById('popup-content');
var closer = document.getElementById('popup-closer');
var sketch;
closer.onclick = function() {
container.style.display = 'none';
closer.blur();
return false;
};
var overlayPopup = new ol.Overlay({
element: container
});
var expandedAttribution = new ol.control.Attribution({
collapsible: false
});
@MapTitle@
var map = new ol.Map({
controls: ol.control.defaults({attribution:false}).extend([
@CONTROLS@
]),
target: document.getElementById('map'),
renderer: 'canvas',
overlays: [overlayPopup],
layers: layersList,
view: new ol.View({
@VIEW@
})
});
@LAYERSLIST@
@LAYERSEARCH@
map.getView().fit(@BOUNDS@, map.getSize());
var NO_POPUP = 0
var ALL_FIELDS = 1
/**
* Returns either NO_POPUP, ALL_FIELDS or the name of a single field to use for
* a given layer
* @param layerList {Array} List of ol.Layer instances
* @param layer {ol.Layer} Layer to find field info about
*/
function getPopupFields(layerList, layer) {
// Determine the index that the layer will have in the popupLayers Array,
// if the layersList contains more items than popupLayers then we need to
// adjust the index to take into account the base maps group
var idx = layersList.indexOf(layer) - (layersList.length - popupLayers.length);
return popupLayers[idx];
}
var collection = new ol.Collection();
var featureOverlay = new ol.layer.Vector({
map: map,
source: new ol.source.Vector({
features: collection,
useSpatialIndex: false // optional, might improve performance
}),
style: [new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#f00',
width: 1
}),
fill: new ol.style.Fill({
color: 'rgba(255,0,0,0.1)'
}),
})],
updateWhileAnimating: true, // optional, for instant visual feedback
updateWhileInteracting: true // optional, for instant visual feedback
});
var doHighlight = @DOHIGHLIGHT@;
var doHover = @ONHOVER@;
var highlight;
var onPointerMove = function(evt) {
if (!doHover && !doHighlight) {
return;
}
var pixel = map.getEventPixel(evt.originalEvent);
var coord = evt.coordinate;
var popupField;
var currentFeature;
var currentLayer;
var currentFeatureKeys;
var clusteredFeatures;
var popupText = '<ul>';
map.forEachFeatureAtPixel(pixel, function(feature, layer) {
// We only care about features from layers in the layersList, ignore
// any other layers which the map might contain such as the vector
// layer used by the measure tool
if (layersList.indexOf(layer) === -1) {
return;
}
var doPopup = false;
for (k in layer.get('fieldImages')) {
if (layer.get('fieldImages')[k] != "Hidden") {
doPopup = true;
}
}
currentFeature = feature;
currentLayer = layer;
clusteredFeatures = feature.get("features");
var clusterFeature;
if (typeof clusteredFeatures !== "undefined") {
if (doPopup) {
for(var n=0; n<clusteredFeatures.length; n++) {
clusterFeature = clusteredFeatures[n];
currentFeatureKeys = clusterFeature.getKeys();
popupText += '<li><table>'
for (var i=0; i<currentFeatureKeys.length; i++) {
if (currentFeatureKeys[i] != 'geometry') {
popupField = '';
if (layer.get('fieldLabels')[currentFeatureKeys[i]] == "inline label") {
popupField += '<th>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</th><td>';
} else {
popupField += '<td colspan="2">';
}
if (layer.get('fieldLabels')[currentFeatureKeys[i]] == "header label") {
popupField += '<strong>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</strong><br />';
}
if (layer.get('fieldImages')[currentFeatureKeys[i]] != "Photo") {
popupField += (clusterFeature.get(currentFeatureKeys[i]) != null ? Autolinker.link(String(clusterFeature.get(currentFeatureKeys[i]))) + '</td>' : '');
} else {
popupField += (clusterFeature.get(currentFeatureKeys[i]) != null ? '<img src="images/' + clusterFeature.get(currentFeatureKeys[i]).replace(/[\\\/:]/g, '_').trim() + '" /></td>' : '');
}
popupText += '<tr>' + popupField + '</tr>';
}
}
popupText += '</table></li>';
}
}
} else {
currentFeatureKeys = currentFeature.getKeys();
if (doPopup) {
popupText += '<li><table>';
for (var i=0; i<currentFeatureKeys.length; i++) {
if (currentFeatureKeys[i] != 'geometry') {
popupField = '';
if (layer.get('fieldLabels')[currentFeatureKeys[i]] == "inline label") {
popupField += '<th>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</th><td>';
} else {
popupField += '<td colspan="2">';
}
if (layer.get('fieldLabels')[currentFeatureKeys[i]] == "header label") {
popupField += '<strong>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</strong><br />';
}
if (layer.get('fieldImages')[currentFeatureKeys[i]] != "Photo") {
popupField += (currentFeature.get(currentFeatureKeys[i]) != null ? Autolinker.link(String(currentFeature.get(currentFeatureKeys[i]))) + '</td>' : '');
} else {
popupField += (currentFeature.get(currentFeatureKeys[i]) != null ? '<img src="images/' + currentFeature.get(currentFeatureKeys[i]).replace(/[\\\/:]/g, '_').trim() + '" /></td>' : '');
}
popupText += '<tr>' + popupField + '</tr>';
}
}
popupText += '</table></li>';
}
}
});
if (popupText == '<ul>') {
popupText = '';
} else {
popupText += '</ul>';
}
if (doHighlight) {
if (currentFeature !== highlight) {
if (highlight) {
featureOverlay.getSource().removeFeature(highlight);
}
if (currentFeature) {
var styleDefinition = currentLayer.getStyle().toString();
if (currentFeature.getGeometry().getType() == 'Point') {
var radius = styleDefinition.split('radius')[1].split(' ')[1];
highlightStyle = new ol.style.Style({
image: new ol.style.Circle({
fill: new ol.style.Fill({
color: "@HIGHLIGHTFILL@"
}),
radius: radius
})
})
} else if (currentFeature.getGeometry().getType() == 'LineString') {
var featureWidth = styleDefinition.split('width')[1].split(' ')[1].replace('})','');
highlightStyle = new ol.style.Style({
stroke: new ol.style.Stroke({
color: '@HIGHLIGHTFILL@',
lineDash: null,
width: featureWidth
})
});
} else {
highlightStyle = new ol.style.Style({
fill: new ol.style.Fill({
color: '@HIGHLIGHTFILL@'
})
})
}
featureOverlay.getSource().addFeature(currentFeature);
featureOverlay.setStyle(highlightStyle);
}
highlight = currentFeature;
}
}
if (doHover) {
if (popupText) {
overlayPopup.setPosition(coord);
content.innerHTML = popupText;
container.style.display = 'block';
} else {
container.style.display = 'none';
closer.blur();
}
}
};
var onSingleClick = function(evt) {
if (doHover) {
return;
}
if (sketch) {
return;
}
var pixel = map.getEventPixel(evt.originalEvent);
var coord = evt.coordinate;
var popupField;
var currentFeature;
var currentFeatureKeys;
var clusteredFeatures;
var popupText = '<ul>';
map.forEachFeatureAtPixel(pixel, function(feature, layer) {
if (feature instanceof ol.Feature && (layer.get("interactive") || layer.get("interactive") == undefined)) {
var doPopup = false;
for (k in layer.get('fieldImages')) {
if (layer.get('fieldImages')[k] != "Hidden") {
doPopup = true;
}
}
currentFeature = feature;
clusteredFeatures = feature.get("features");
var clusterFeature;
if (typeof clusteredFeatures !== "undefined") {
if (doPopup) {
for(var n=0; n<clusteredFeatures.length; n++) {
clusterFeature = clusteredFeatures[n];
currentFeatureKeys = clusterFeature.getKeys();
popupText += '<li><table>'
for (var i=0; i<currentFeatureKeys.length; i++) {
if (currentFeatureKeys[i] != 'geometry') {
popupField = '';
if (layer.get('fieldLabels')[currentFeatureKeys[i]] == "inline label") {
popupField += '<th>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</th><td>';
} else {
popupField += '<td colspan="2">';
}
if (layer.get('fieldLabels')[currentFeatureKeys[i]] == "header label") {
popupField += '<strong>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</strong><br />';
}
if (layer.get('fieldImages')[currentFeatureKeys[i]] != "Photo") {
popupField += (clusterFeature.get(currentFeatureKeys[i]) != null ? Autolinker.link(String(clusterFeature.get(currentFeatureKeys[i]))) + '</td>' : '');
} else {
popupField += (clusterFeature.get(currentFeatureKeys[i]) != null ? '<img src="images/' + clusterFeature.get(currentFeatureKeys[i]).replace(/[\\\/:]/g, '_').trim() + '" /></td>' : '');
}
popupText += '<tr>' + popupField + '</tr>';
}
}
popupText += '</table></li>';
}
}
} else {
currentFeatureKeys = currentFeature.getKeys();
if (doPopup) {
popupText += '<li><table>';
for (var i=0; i<currentFeatureKeys.length; i++) {
if (currentFeatureKeys[i] != 'geometry') {
popupField = '';
if (layer.get('fieldLabels')[currentFeatureKeys[i]] == "inline label") {
popupField += '<th>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</th><td>';
} else {
popupField += '<td colspan="2">';
}
if (layer.get('fieldLabels')[currentFeatureKeys[i]] == "header label") {
popupField += '<strong>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</strong><br />';
}
if (layer.get('fieldImages')[currentFeatureKeys[i]] != "Photo") {
popupField += (currentFeature.get(currentFeatureKeys[i]) != null ? Autolinker.link(String(currentFeature.get(currentFeatureKeys[i]))) + '</td>' : '');
} else {
popupField += (currentFeature.get(currentFeatureKeys[i]) != null ? '<img src="images/' + currentFeature.get(currentFeatureKeys[i]).replace(/[\\\/:]/g, '_').trim() + '" /></td>' : '');
}
popupText += '<tr>' + popupField + '</tr>';
}
}
popupText += '</table>';
}
}
}
});
if (popupText == '<ul>') {
popupText = '';
} else {
popupText += '</ul>';
}
var viewProjection = map.getView().getProjection();
var viewResolution = map.getView().getResolution();
for (i = 0; i < wms_layers.length; i++) {
if (wms_layers[i][1]) {
var url = wms_layers[i][0].getSource().getGetFeatureInfoUrl(
evt.coordinate, viewResolution, viewProjection,
{
'INFO_FORMAT': 'text/html',
});
if (url) {
popupText = popupText + '<iframe style="width:100%;height:110px;border:0px;" id="iframe" seamless src="' + url + '"></iframe>';
}
}
}
if (popupText) {
overlayPopup.setPosition(coord);
content.innerHTML = popupText;
container.style.display = 'block';
} else {
container.style.display = 'none';
closer.blur();
}
};
@MEASURING@
map.on('pointermove', function(evt) {
onPointerMove(evt);
});
map.on('singleclick', function(evt) {
onSingleClick(evt);
});
@MEASURE@
@MEASUREUNIT@
@GEOLOCATE@
@GEOCODINGSCRIPT@@MAPUNITLAYERS@
var attributionComplete = false;
map.on("rendercomplete", function(evt) {
if (!attributionComplete) {
var attribution = document.getElementsByClassName('ol-attribution')[0];
var attributionList = attribution.getElementsByTagName('ul')[0];
var firstLayerAttribution = attributionList.getElementsByTagName('li')[0];
var qgis2webAttribution = document.createElement('li');
qgis2webAttribution.innerHTML = '<a href="https://github.com/tomchadwin/qgis2web">qgis2web</a> &middot; ';
var olAttribution = document.createElement('li');
olAttribution.innerHTML = '<a href="https://openlayers.org/">OpenLayers</a> &middot; ';
var qgisAttribution = document.createElement('li');
qgisAttribution.innerHTML = '<a href="https://qgis.org/">QGIS</a>';
attributionList.insertBefore(qgis2webAttribution, firstLayerAttribution);
attributionList.insertBefore(olAttribution, firstLayerAttribution);
attributionList.insertBefore(qgisAttribution, firstLayerAttribution);
attributionComplete = true;
}
})
@M2PX@@GRID@
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment