From 68f6b612172a6f738096bf27b6e5b57723f11b89 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Sat, 29 Jul 2017 17:56:48 +0200 Subject: [PATCH] Address review comments --- examples/street-labels.html | 9 +- examples/street-labels.js | 131 +++++++----------------- examples/vector-label-decluttering.html | 1 - examples/vector-label-decluttering.js | 10 +- externs/olx.js | 2 +- src/ol/render/canvas/replay.js | 4 +- src/ol/style/style.js | 2 +- 7 files changed, 48 insertions(+), 111 deletions(-) diff --git a/examples/street-labels.html b/examples/street-labels.html index 6247beaca7..bab0d1b061 100644 --- a/examples/street-labels.html +++ b/examples/street-labels.html @@ -3,18 +3,15 @@ layout: example.html title: Street Labels shortdesc: Render street names with a custom render. docs: > - Example showing the use of a custom renderer to render text along a path. [Labelgun](https://github.com/Geovation/labelgun) is used to avoid label collisions, and [linelabel](https://github.com/naturalatlas/linelabel) makes sure that labels are placed on suitable street segments. The data is fetched from OSM using the [Overpass API](https://overpass-api.de). + Example showing the use of a custom renderer to render text along a path. [Labelgun](https://github.com/Geovation/labelgun) is used to avoid label collisions. [label-segment](https://github.com/ahocevar/label-segment) makes sure that labels are placed on suitable street segments. [textpath](https://github.com/ahocevar/textpath) arranges the letters of a label along the geometry. The data is fetched from OSM using the [Overpass API](https://overpass-api.de). tags: "vector, label, collision detection, labelgun, linelabel, overpass" resources: - https://cdn.polyfill.io/v2/polyfill.min.js?features=Set" - https://unpkg.com/rbush@2.0.1/rbush.min.js - https://unpkg.com/labelgun@0.1.1/lib/labelgun.min.js + - https://unpkg.com/textpath@1.0.1/dist/textpath.js + - https://unpkg.com/label-segment@1.0.0/dist/label-segment.js cloak: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5: Your Bing Maps Key from http://www.bingmapsportal.com/ here --- - - - - -
diff --git a/examples/street-labels.js b/examples/street-labels.js index c2b029225e..464cd2785d 100644 --- a/examples/street-labels.js +++ b/examples/street-labels.js @@ -1,108 +1,45 @@ // NOCOMPILE +/* global labelgun, labelSegment, textPath */ goog.require('ol.Map'); goog.require('ol.View'); goog.require('ol.extent'); goog.require('ol.format.OSMXML'); -goog.require('ol.geom.LineString'); goog.require('ol.layer.Tile'); goog.require('ol.layer.Vector'); goog.require('ol.source.BingMaps'); goog.require('ol.source.Vector'); goog.require('ol.style.Style'); -/* global labelgun */ -var labelEngine = new labelgun['default'](function() {}, function() {}); +var emptyFn = function() {}; +var labelEngine = new labelgun['default'](emptyFn, emptyFn); -function segmentSort(a, b) { - return a.length - b.length; +var context, pixelRatio; // Will be set in the map's postcompose listener +function measureText(text) { + return context.measureText(text).width * pixelRatio; } -function dist2D(p1, p2) { - var dx = p2[0] - p1[0]; - var dy = p2[1] - p1[1]; - return Math.sqrt(dx * dx + dy * dy); -} - -// Modified from https://github.com/Viglino/ol3-ext/blob/7d17eef5720970fd36798ebc889ea44d9f04b059/style/settextpathstyle.js -function textPath(ctx, text, path, fid, pixelRatio) { - var di, dpos = 0; - var pos = 1; - var letterPadding = ctx.measureText(' ').width * pixelRatio * 0.25; - var d = 0; - for (var i = 1; i < path.length; ++i) { - d += dist2D(path[i - 1], path[i]); - } - var nbspace = text.split(' ').length - 1; - var start = (d - ctx.measureText(text).width * pixelRatio - (text.length + nbspace) * letterPadding) / 2; - var extent = ol.extent.createEmpty(); - var letters = []; - for (var t = 0; t < text.length; t++) { - var letter = text[t]; - var wl = ctx.measureText(letter).width * pixelRatio; - var dl = start + wl / 2; - if (!di || dpos + di < dl) { - for (; pos < path.length;) { - di = dist2D(path[pos - 1], path[pos]); - if (dpos + di > dl) { - break; - } - pos += 1; - if (pos >= path.length) { - break; - } - dpos += di; - } - } - var x, y, a, dt = dl - dpos; - if (pos >= path.length) { - pos = path.length - 1; - } - a = Math.atan2(path[pos][1] - path[pos - 1][1], path[pos][0] - path[pos - 1][0]); - if (!dt) { - x = path[pos - 1][0]; - y = path[pos - 1][1]; - } else { - x = path[pos - 1][0] + (path[pos][0] - path[pos - 1][0]) * dt / di; - y = path[pos - 1][1] + (path[pos][1] - path[pos - 1][1]) * dt / di; - } - ol.extent.extendCoordinate(extent, [x, y]); - letters.push([x, y, a, letter]); - start += wl + letterPadding * (letter == ' ' ? 2 : 1); - } - ol.extent.buffer(extent, 5 * pixelRatio, extent); - var bounds = { - bottomLeft: ol.extent.getBottomLeft(extent), - topRight: ol.extent.getTopRight(extent) - }; - labelEngine.ingestLabel(bounds, fid, 1, letters, text, false); +var extent, letters; // Will be set in the style's renderer function +function collectDrawData(letter, x, y, angle) { + ol.extent.extendCoordinate(extent, [x, y]); + letters.push([x, y, angle, letter]); } var style = new ol.style.Style({ - geometry: function(feature) { - // Use the longest, straight enough segment of the geometry - var coords = feature.getGeometry().getCoordinates(); - /* global linelabel */ - var segment = linelabel(coords, Math.PI / 8).sort(segmentSort)[0]; - return new ol.geom.LineString(coords.slice(segment.beginIndex, segment.endIndex)); - }, - renderer: function(coords, geometry, feature, state) { - var context = state.context; - var pixelRatio = state.pixelRatio; + renderer: function(coords, geometry, feature) { var text = feature.get('name'); if (text) { - // Only consider label when the segment is long enough - var labelLength = context.measureText(text).width * pixelRatio; - var pathLength = 0; - for (var i = 1, ii = coords.length; i < ii; ++i) { - pathLength += dist2D(coords[i - 1], coords[i]); - if (pathLength >= labelLength) { - if (coords[0][0] > coords[coords.length - 1][0]) { - // Attempt to make text upright - coords.reverse(); - } - textPath(context, text, coords, feature.getId(), pixelRatio); - break; - } + // Only create label when geometry has a long and straight segment + var path = labelSegment(coords, Math.PI / 8, measureText(text)); + if (path) { + extent = ol.extent.createEmpty(); + letters = []; + textPath(text, path, measureText, collectDrawData); + ol.extent.buffer(extent, 5 * pixelRatio, extent); + var bounds = { + bottomLeft: ol.extent.getBottomLeft(extent), + topRight: ol.extent.getTopRight(extent) + }; + labelEngine.ingestLabel(bounds, feature.getId(), 1, letters, text, false); } } } @@ -117,15 +54,17 @@ var rasterLayer = new ol.layer.Tile({ var source = new ol.source.Vector(); // Request streets from OSM, using the Overpass API -var client = new XMLHttpRequest(); -client.open('POST', 'https://overpass-api.de/api/interpreter'); -client.addEventListener('load', function() { - var features = new ol.format.OSMXML().readFeatures(client.responseText, { +fetch('https://overpass-api.de/api/interpreter', { + method: 'POST', + body: '(way["highway"](48.19642,16.32580,48.22050,16.41986));(._;>;);out meta;' +}).then(function(response) { + return response.text(); +}).then(function(responseText) { + var features = new ol.format.OSMXML().readFeatures(responseText, { featureProjection: 'EPSG:3857' }); source.addFeatures(features); }); -client.send('(way["highway"](48.19642,16.32580,48.22050,16.41986));(._;>;);out meta;'); var vectorLayer = new ol.layer.Vector({ source: source, @@ -136,13 +75,13 @@ var vectorLayer = new ol.layer.Vector({ } }); -var extent = [1817379, 6139595, 1827851, 6143616]; +var viewExtent = [1817379, 6139595, 1827851, 6143616]; var map = new ol.Map({ layers: [rasterLayer, vectorLayer], target: 'map', view: new ol.View({ - extent: extent, - center: ol.extent.getCenter(extent), + extent: viewExtent, + center: ol.extent.getCenter(viewExtent), zoom: 17, minZoom: 14 }) @@ -152,8 +91,8 @@ vectorLayer.on('precompose', function() { labelEngine.destroy(); }); vectorLayer.on('postcompose', function(e) { - var context = e.context; - var pixelRatio = e.frameState.pixelRatio; + context = e.context; + pixelRatio = e.frameState.pixelRatio; context.save(); context.font = 'normal 11px "Open Sans", "Arial Unicode MS"'; context.fillStyle = 'white'; diff --git a/examples/vector-label-decluttering.html b/examples/vector-label-decluttering.html index 911e5e9f0f..cc45ca1d74 100644 --- a/examples/vector-label-decluttering.html +++ b/examples/vector-label-decluttering.html @@ -11,4 +11,3 @@ docs: > tags: "vector, renderer, labelgun, label" ---
-
 
diff --git a/examples/vector-label-decluttering.js b/examples/vector-label-decluttering.js index 11b2f92650..9a50d04dd9 100644 --- a/examples/vector-label-decluttering.js +++ b/examples/vector-label-decluttering.js @@ -1,4 +1,5 @@ // NOCOMPILE +/* global labelgun */ goog.require('ol.Map'); goog.require('ol.View'); goog.require('ol.extent'); @@ -38,8 +39,8 @@ var map = new ol.Map({ }) }); -/*global labelgun*/ -var labelEngine = new labelgun['default'](function() {}, function() {}); +var emptyFn = function() {}; +var labelEngine = new labelgun['default'](emptyFn, emptyFn); function createLabel(canvas, text, coord) { var halfWidth = canvas.width / 2; @@ -57,7 +58,7 @@ function sortByWidth(a, b) { return ol.extent.getWidth(b.getExtent()) - ol.extent.getWidth(a.getExtent()); } -var resolution; +var resolution; // This is set by the map's precompose listener var styles = [ new ol.style.Style({ fill: new ol.style.Fill({ @@ -85,6 +86,7 @@ var styles = [ context.strokeText(text, 0, 0); context.fillText(text, 0, 0); } + // The 3rd value of the coordinate is the measure of the extent width var extentWidth = geometry.getCoordinates()[2] / resolution * pixelRatio; if (extentWidth > canvas.width) { // Only consider labels not wider than their country's bounding box @@ -100,6 +102,7 @@ var styles = [ } var coordinates = geometry.getInteriorPoint().getCoordinates(); var extentWidth = ol.extent.getWidth(geometry.getExtent()); + // We are using the extentWidth as measure value of the geometry coordinates.push(extentWidth); return new ol.geom.Point(coordinates, 'XYM'); } @@ -126,5 +129,4 @@ vectorLayer.on('postcompose', function(e) { } }); - map.addLayer(vectorLayer); diff --git a/externs/olx.js b/externs/olx.js index 6bae671aa6..e07514a3ea 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -4426,7 +4426,7 @@ olx.render.State.prototype.pixelRatio; /** - * Resolution that the render batch was created and optiized for. This is + * Resolution that the render batch was created and optimized for. This is * not the view's resolution that is being rendered. * @type {number} * @api diff --git a/src/ol/render/canvas/replay.js b/src/ol/render/canvas/replay.js index a02b32d321..309ce70586 100644 --- a/src/ol/render/canvas/replay.js +++ b/src/ol/render/canvas/replay.js @@ -324,7 +324,7 @@ ol.render.canvas.Replay.prototype.replay_ = function( /** * @type {olx.render.State} */ - var replayState = { + var state = { context: context, pixelRatio: pixelRatio, resolution: this.resolution, @@ -397,7 +397,7 @@ ol.render.canvas.Replay.prototype.replay_ = function( } else { coords = pixelCoordinates.slice(d, dd); } - renderer(coords, geometry, feature, replayState); + renderer(coords, geometry, feature, state); ++i; break; case ol.render.canvas.Instruction.DRAW_IMAGE: diff --git a/src/ol/style/style.js b/src/ol/style/style.js index 3d1469355a..09dd93407f 100644 --- a/src/ol/style/style.js +++ b/src/ol/style/style.js @@ -111,7 +111,7 @@ ol.style.Style.prototype.getRenderer = function() { /** * Sets a custom renderer function for this style. When set, `fill`, `stroke` - * and `image` optins of the style will be ignored. + * and `image` options of the style will be ignored. * @param {ol.StyleRenderFunction|null} renderer Custom renderer function. * @api */