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

Added scalebar option to ol.control.scaleline #9013

Merged
merged 3 commits into from Jan 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 16 additions & 0 deletions examples/scale-line.html
Expand Up @@ -14,3 +14,19 @@
<option value="nautical">nautical mile</option>
<option value="metric" selected>metric</option>
</select>

<select id="type">
<option value="scaleline">ScaleLine</option>
<option value="scalebar">ScaleBar</option>
</select>

<select id="steps" style="display:none">
<option value=2>2 steps</option>
<option value=4 selected>4 steps</option>
<option value=6>6 steps</option>
<option value=8>8 steps</option>
</select>

<div id="showScaleTextDiv" style="display:none">
<input type="checkbox" id="showScaleText" checked>Show scale text
</div>
60 changes: 54 additions & 6 deletions examples/scale-line.js
Expand Up @@ -4,12 +4,36 @@ import {defaults as defaultControls, ScaleLine} from '../src/ol/control.js';
import TileLayer from '../src/ol/layer/Tile.js';
import OSM from '../src/ol/source/OSM.js';

const unitsSelect = document.getElementById('units');
const typeSelect = document.getElementById('type');
const stepsSelect = document.getElementById('steps');
const scaleTextCheckbox = document.getElementById('showScaleText');
const showScaleTextDiv = document.getElementById('showScaleTextDiv');

const scaleLineControl = new ScaleLine();
let scaleType = 'scaleline';
let scaleBarSteps = 4;
let scaleBarText = true;
let control;

function scaleControl() {
if (scaleType === 'scaleline') {
control = new ScaleLine({
units: unitsSelect.value
});
return control;
}
tschaub marked this conversation as resolved.
Show resolved Hide resolved
control = new ScaleLine({
units: unitsSelect.value,
bar: true,
steps: scaleBarSteps,
text: scaleBarText,
minWidth: 140
});
return control;
}
const map = new Map({
controls: defaultControls().extend([
scaleLineControl
scaleControl()
]),
layers: [
new TileLayer({
Expand All @@ -23,10 +47,34 @@ const map = new Map({
})
});


const unitsSelect = document.getElementById('units');
function onChange() {
scaleLineControl.setUnits(unitsSelect.value);
control.setUnits(unitsSelect.value);
}
function onChangeType() {
scaleType = typeSelect.value;
if (typeSelect.value === 'scalebar') {
stepsSelect.style.display = 'inline';
showScaleTextDiv.style.display = 'inline';
map.removeControl(control);
map.addControl(scaleControl());
} else {
stepsSelect.style.display = 'none';
showScaleTextDiv.style.display = 'none';
map.removeControl(control);
map.addControl(scaleControl());
}
}
function onChangeSteps() {
scaleBarSteps = parseInt(stepsSelect.value, 10);
map.removeControl(control);
map.addControl(scaleControl());
}
function onChangeScaleText() {
scaleBarText = scaleTextCheckbox.checked;
map.removeControl(control);
map.addControl(scaleControl());
}
unitsSelect.addEventListener('change', onChange);
onChange();
typeSelect.addEventListener('change', onChangeType);
stepsSelect.addEventListener('change', onChangeSteps);
scaleTextCheckbox.addEventListener('change', onChangeScaleText);
154 changes: 151 additions & 3 deletions src/ol/control/ScaleLine.js
Expand Up @@ -45,6 +45,11 @@ const LEADING_DIGITS = [1, 2, 5];
* @property {HTMLElement|string} [target] Specify a target if you want the control
* to be rendered outside of the map's viewport.
* @property {Units|string} [units='metric'] Units.
* @property {boolean} [bar=false] Render scalebars instead of a line.
* @property {number} [steps=4] Number of steps the scalebar should use. Use even numbers
* for best results. Only applies when `bar` is `true`.
* @property {boolean} [text=false] Render the text scale above of the scalebar. Only applies
* when `bar` is `true`.
*/


Expand All @@ -57,6 +62,8 @@ const LEADING_DIGITS = [1, 2, 5];
* viewport center cannot be calculated in the view projection.
* By default the scale line will show in the bottom left portion of the map,
* but this can be changed by using the css selector `.ol-scale-line`.
* When specifying `bar` as `true`, a scalebar will be rendered instead
* of a scaleline.
*
* @api
*/
Expand All @@ -69,7 +76,8 @@ class ScaleLine extends Control {

const options = opt_options ? opt_options : {};

const className = options.className !== undefined ? options.className : 'ol-scale-line';
const className = options.className !== undefined ? options.className :
options.bar ? 'ol-scale-bar' : 'ol-scale-line';

super({
element: document.createElement('div'),
Expand Down Expand Up @@ -123,6 +131,24 @@ class ScaleLine extends Control {

this.setUnits(/** @type {Units} */ (options.units) || Units.METRIC);

/**
* @private
* @type {boolean}
*/
this.scaleBar_ = options.bar || false;

/**
* @private
* @type {number}
*/
this.scaleBarSteps_ = options.steps || 4;

/**
* @private
* @type {boolean}
*/
this.scaleBarText_ = options.text || false;

}

/**
Expand Down Expand Up @@ -257,7 +283,13 @@ class ScaleLine extends Control {
++i;
}

const html = count + ' ' + suffix;
let html;
if (this.scaleBar_) {
html = this.createScaleBar(width, count, suffix);
} else {
html = count + ' ' + suffix;
}

if (this.renderedHTML_ != html) {
this.innerElement_.innerHTML = html;
this.renderedHTML_ = html;
Expand All @@ -274,8 +306,124 @@ class ScaleLine extends Control {
}

}
}

/**
* @private
* @param {number} width The current width of the scalebar.
* @param {number} scale The current scale.
* @param {string} suffix The suffix to append to the scale text.
* @returns {string} The stringified HTML of the scalebar.
*/
createScaleBar(width, scale, suffix) {
const mapScale = '1 : ' + Math.round(this.getScaleForResolution()).toLocaleString();
const scaleSteps = [];
const stepWidth = width / this.scaleBarSteps_;
let backgroundColor = '#ffffff';
for (let i = 0; i < this.scaleBarSteps_; i++) {
if (i === 0) {
// create the first marker at position 0
scaleSteps.push(this.createMarker('absolute', i));
}
scaleSteps.push(
'<div>' +
'<div ' +
'class="ol-scale-singlebar" ' +
'style=' +
'"width: ' + stepWidth + 'px;' +
'background-color: ' + backgroundColor + ';"' +
'>' +
'</div>' +
this.createMarker('relative', i) +
/*render text every second step, except when only 2 steps */
(i % 2 === 0 || this.scaleBarSteps_ === 2 ?
this.createStepText(i, width, false, scale, suffix) :
''
) +
'</div>'
);
if (i === this.scaleBarSteps_ - 1) {
{/*render text at the end */}
scaleSteps.push(this.createStepText(i + 1, width, true, scale, suffix));
}
// switch colors of steps between black and white
if (backgroundColor === '#ffffff') {
backgroundColor = '#000000';
} else {
backgroundColor = '#ffffff';
}
}

let scaleBarText;
if (this.scaleBarText_) {
scaleBarText = '<div ' +
'class="ol-scale-text" ' +
'style="width: ' + width + 'px;">' +
mapScale +
'</div>';
} else {
scaleBarText = '';
}
const container = '<div ' +
'style="display: flex;">' +
scaleBarText +
scaleSteps.join('') +
'</div>';
return container;
}

/**
* Creates a marker at given position
* @param {string} position - The position, absolute or relative
* @param {number} i - The iterator
* @returns {string} The stringified div containing the marker
*/
createMarker(position, i) {
const top = position === 'absolute' ? 3 : -10;
return '<div ' +
'class="ol-scale-step-marker" ' +
'style="position: ' + position + ';' +
'top: ' + top + 'px;"' +
'></div>';
}

/**
* Creates the label for a marker marker at given position
* @param {number} i - The iterator
* @param {number} width - The width the scalebar will currently use
* @param {boolean} isLast - Flag indicating if we add the last step text
* @param {number} scale - The current scale for the whole scalebar
* @param {string} suffix - The suffix for the scale
* @returns {string} The stringified div containing the step text
*/
createStepText(i, width, isLast, scale, suffix) {
const length = i === 0 ? 0 : Math.round((scale / this.scaleBarSteps_ * i) * 100) / 100;
const lengthString = length + (i === 0 ? '' : ' ' + suffix);
const margin = i === 0 ? -3 : width / this.scaleBarSteps_ * -1;
const minWidth = i === 0 ? 0 : width / this.scaleBarSteps_ * 2;
return '<div ' +
'class="ol-scale-step-text" ' +
'style="' +
'margin-left: ' + margin + 'px;' +
'text-align: ' + (i === 0 ? 'left' : 'center') + '; ' +
'min-width: ' + minWidth + 'px;' +
'left: ' + (isLast ? width + 'px' : 'unset') + ';"' +
'>' +
lengthString +
'</div>';
}

/**
* Returns the appropriate scale for the given resolution and units.
* @return {number} The appropriate scale.
*/
getScaleForResolution() {
const resolution = this.getMap().getView().getResolution();
const dpi = 25.4 / 0.28;
const mpu = this.viewState_.projection.getMetersPerUnit();
const inchesPerMeter = 39.37;
return parseFloat(resolution.toString()) * mpu * inchesPerMeter * dpi;
}
}

/**
* Update the scale line element.
Expand Down
36 changes: 36 additions & 0 deletions src/ol/ol.css
Expand Up @@ -26,7 +26,43 @@
text-align: center;
margin: 1px;
will-change: contents, width;
transition: all 0.25s;
}
.ol-scale-bar {
position: absolute;
bottom: 8px;
left: 8px;
}
.ol-scale-step-marker {
width: 1px;
height: 15px;
background-color: #000000;
float: right;
z-Index: 10;
}
.ol-scale-step-text {
position: absolute;
bottom: -5px;
font-size: 12px;
z-Index: 11;
color: #000000;
text-shadow: -2px 0 #FFFFFF, 0 2px #FFFFFF, 2px 0 #FFFFFF, 0 -2px #FFFFFF;
}
.ol-scale-text {
position: absolute;
font-size: 14px;
text-align: center;
bottom: 25px;
color: #000000;
text-shadow: -2px 0 #FFFFFF, 0 2px #FFFFFF, 2px 0 #FFFFFF, 0 -2px #FFFFFF;
}
.ol-scale-singlebar {
position: relative;
height: 10px;
z-Index: 9;
border: 1px solid black;
}

.ol-overlay-container {
will-change: left,right,top,bottom;
}
Expand Down