Skip to content

Commit

Permalink
Merge pull request #628 from OpenGeoscience/scaled-point-annotations
Browse files Browse the repository at this point in the history
Allow point annotations to scale with zoom.
  • Loading branch information
manthey authored Oct 20, 2016
2 parents 33cb7a4 + 72e3135 commit 27b090e
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 11 deletions.
3 changes: 3 additions & 0 deletions examples/annotations/index.jade
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ block append mainContent
.form-group(annotation-types='point')
label(for='edit-radius') Radius
input#edit-radius(option='radius', format='positive')
.form-group(annotation-types='point', title='Set to "false" to disable, "true" to use the specified radius at the current zoom, or a zoom level to use the specified radius at that zoom level.')
label(for='edit-scaled') Scale with Zoom
input#edit-radius(option='scaled', format='booleanOrNumber')
.form-group(annotation-types='point polygon rectangle')
label(for='edit-fill') Fill
select#edit-stroke(option='fill', format='boolean')
Expand Down
Binary file added examples/annotations/thumb.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 30 additions & 4 deletions src/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -690,20 +690,27 @@ registerAnnotation('polygon', polygonAnnotation, polygonRequiredFeatures);
* May specify:
* style.
* radius, fill, fillColor, fillOpacity, stroke, strokeWidth, strokeColor,
* strokeOpacity
* strokeOpacity, scaled
*
* If scaled is false, the point is not scaled with zoom level. If it is true,
* the radius is based on the zoom level at first instantiation. Otherwise, if
* it is a number, the radius is used at that zoom level.
*/
/////////////////////////////////////////////////////////////////////////////
var pointAnnotation = function (args) {
'use strict';
if (!(this instanceof pointAnnotation)) {
return new pointAnnotation(args);
}
var m_this = this;

args = $.extend(true, {}, {
style: {
fill: true,
fillColor: {r: 0, g: 1, b: 0},
fillOpacity: 0.25,
radius: 10,
scaled: false,
stroke: true,
strokeColor: {r: 0, g: 0, b: 0},
strokeOpacity: 1,
Expand All @@ -722,17 +729,36 @@ var pointAnnotation = function (args) {
this.features = function () {
var opt = this.options(),
state = this.state(),
features;
features, style, scaleOnZoom;
switch (state) {
case annotationState.create:
features = [];
break;
default:
style = opt.style;
if (opt.style.scaled || opt.style.scaled === 0) {
if (opt.style.scaled === true) {
opt.style.scaled = this.layer().map().zoom();
}
style = $.extend({}, style, {
radius: function () {
var radius = opt.style.radius,
zoom = m_this.layer().map().zoom();
if (util.isFunction(radius)) {
radius = radius.apply(this, arguments);
}
radius *= Math.pow(2, zoom - opt.style.scaled);
return radius;
}
});
scaleOnZoom = true;
}
features = [{
point: {
x: opt.position.x,
y: opt.position.y,
style: opt.style
style: style,
scaleOnZoom: scaleOnZoom
}
}];
break;
Expand Down Expand Up @@ -786,7 +812,7 @@ var pointAnnotation = function (args) {
* @return {array} a list of style names to store.
*/
this._geojsonStyles = function () {
return ['fill', 'fillColor', 'fillOpacity', 'radius', 'stroke',
return ['fill', 'fillColor', 'fillOpacity', 'radius', 'scaled', 'stroke',
'strokeColor', 'strokeOpacity', 'strokeWidth'];
};

Expand Down
43 changes: 39 additions & 4 deletions src/annotationLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ var annotationLayer = function (args) {
'fillColor': {dataType: 'color', keys: ['fillColor', 'fill-color', 'marker-color', 'fill']},
'fillOpacity': {dataType: 'opacity', keys: ['fillOpacity', 'fill-opacity']},
'radius': {dataType: 'positive', keys: ['radius']},
'scaled': {dataType: 'booleanOrNumber', keys: ['scaled']},
'stroke': {dataType: 'boolean', keys: ['stroke']},
'strokeColor': {dataType: 'color', keys: ['strokeColor', 'stroke-color', 'stroke']},
'strokeOpacity': {dataType: 'opacity', keys: ['strokeOpacity', 'stroke-opacity']},
Expand Down Expand Up @@ -546,8 +547,13 @@ var annotationLayer = function (args) {
* object with r, g, b properties in the range of [0-1].
* opacity: a floating point number in the range [0, 1].
* positive: a floating point number greater than zero.
* boolean: the string 'false' and falsy values are false, all else is
* true. null and undefined are still considered invalid values.
* boolean: a string whose lowercase value is 'false', 'off', or 'no', and
* falsy values are false, all else is true. null and undefined are
* still considered invalid values.
* booleanOrNumber: a string whose lowercase value is 'false', 'off', no',
* 'true', 'on', or 'yes', falsy values that aren't 0, and true are
* handled as booleans. Otherwise, a floating point number that isn't
* NaN or an infinity.
* @param {number|string|object|boolean} value: the value to validate.
* @param {string} dataType: the data type for validation.
* @returns {number|string|object|boolean|undefined} the sanitized value or
Expand All @@ -558,8 +564,18 @@ var annotationLayer = function (args) {
return;
}
switch (dataType) {
case 'booleanOrNumber':
if ((!value && value !== 0) || ['true', 'false', 'off', 'on', 'no', 'yes'].indexOf(('' + value).toLowerCase()) >= 0) {
value = !!value && ['false', 'no', 'off'].indexOf(('' + value).toLowerCase()) < 0;
} else {
value = +value;
if (isNaN(value) || !isFinite(value)) {
return;
}
}
break;
case 'boolean':
value = !!value && value !== 'false';
value = !!value && ['false', 'no', 'off'].indexOf(('' + value).toLowerCase()) < 0;
break;
case 'color':
value = util.convertColor(value);
Expand All @@ -575,7 +591,7 @@ var annotationLayer = function (args) {
break;
case 'positive':
value = +value;
if (isNaN(value) || value <= 0) {
if (isNaN(value) || !isFinite(value) || value <= 0) {
return;
}
break;
Expand All @@ -599,6 +615,7 @@ var annotationLayer = function (args) {
$.each(m_features, function (idx, featureLevel) {
$.each(featureLevel, function (type, feature) {
feature.data = [];
delete feature.feature.scaleOnZoom;
});
});
$.each(m_annotations, function (annotation_idx, annotation) {
Expand Down Expand Up @@ -663,6 +680,9 @@ var annotationLayer = function (args) {
}
/* Collect the data for each feature */
m_features[idx][type].data.push(featureSpec.data || featureSpec);
if (featureSpec.scaleOnZoom) {
m_features[idx][type].feature.scaleOnZoom = true;
}
});
});
});
Expand All @@ -677,6 +697,19 @@ var annotationLayer = function (args) {
s_update.call(m_this, request);
};

/**
* Check if any features are marked that they need to be updated when a zoom
* occurs. If so, mark that feature as modified.
*/
this._handleZoom = function () {
var i, features = m_this.features();
for (i = 0; i < features.length; i += 1) {
if (features[i].scaleOnZoom) {
features[i].modified();
}
}
};

///////////////////////////////////////////////////////////////////////////
/**
* Initialize
Expand All @@ -694,6 +727,8 @@ var annotationLayer = function (args) {
m_this.geoOn(geo_event.mouseclick, m_this._handleMouseClick);
m_this.geoOn(geo_event.mousemove, m_this._handleMouseMove);

m_this.geoOn(geo_event.zoom, m_this._handleZoom);

return m_this;
};

Expand Down
26 changes: 25 additions & 1 deletion tests/cases/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ describe('geo.annotation', function () {
});
it('_geojsonStyles', function () {
var ann = geo.annotation.pointAnnotation();
expect(ann._geojsonStyles().length).toBe(8);
expect(ann._geojsonStyles().length).toBe(9);
});
it('_geojsonCoordinates', function () {
var ann = geo.annotation.pointAnnotation();
Expand Down Expand Up @@ -423,6 +423,30 @@ describe('geo.annotation', function () {
expect(ann.options('position')).toEqual({x: 10, y: 20});
expect(ann.state()).toBe(geo.annotation.state.done);
});
it('scaled radius', function () {
var map = create_map();
var layer = map.createLayer('annotation', {
annotations: ['point']
});
var ann = geo.annotation.pointAnnotation({
position: point, layer: layer, style: {scaled: true}});
var features = ann.features();
expect(features.length).toBe(1);
expect(features[0].point.x).toEqual(point.x);
expect(features[0].point.style.radius()).toBe(10);
map.zoom(3);
features = ann.features();
expect(features[0].point.style.radius()).toBe(5);
ann.options().style.radius = function () {
return map.zoom() > 6.5 ? 4 : 10;
};
map.zoom(6);
features = ann.features();
expect(features[0].point.style.radius()).toBe(40);
map.zoom(7);
features = ann.features();
expect(features[0].point.style.radius()).toBe(32);
});
});

describe('annotation registry', function () {
Expand Down
22 changes: 20 additions & 2 deletions tests/cases/annotationLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,20 @@ describe('geo.annotationLayer', function () {
});
expect(layer.annotations()[0].options('vertices')[0]).not.toEqual(layer.annotations()[0].options('vertices')[1]);
});
it('_handleZoom', function () {
layer.mode(null);
layer.removeAllAnnotations();
layer.addAnnotation(point);
layer._update();
var mod = layer.features()[0].getMTime();
layer._handleZoom();
expect(layer.features()[0].getMTime()).toBe(mod);
layer.annotations()[0].options({style: {scaled: true}});
layer._update();
mod = layer.features()[0].getMTime();
layer._handleZoom();
expect(layer.features()[0].getMTime()).toBeGreaterThan(mod);
});
it('_processSelection', function () {
layer.removeAllAnnotations();
layer._processSelection({
Expand Down Expand Up @@ -467,14 +481,16 @@ describe('geo.annotationLayer', function () {
properties: {
radius: -5,
fillColor: 'no such color',
fillOpacity: -1
fillOpacity: -1,
scaled: 'not a number'
}
};
layer.geojson(badattr, true);
var attr = layer.geojson().features[0].properties;
expect(attr.radius).toBeGreaterThan(0);
expect(attr.fillOpacity).toBeGreaterThan(0);
expect(attr.fillColor).toBe('#00ff00');
expect(attr.scaled).toBe(false);
var goodattr = {
type: 'Feature',
geometry: {
Expand All @@ -484,14 +500,16 @@ describe('geo.annotationLayer', function () {
properties: {
radius: 3,
fillColor: 'indigo',
fillOpacity: 0.3
fillOpacity: 0.3,
scaled: 'On'
}
};
layer.geojson(goodattr, true);
attr = layer.geojson().features[0].properties;
expect(attr.radius).toBe(3);
expect(attr.fillOpacity).toBe(0.3);
expect(attr.fillColor).toBe('#4b0082');
expect(attr.scaled).toBe(4);
});
});
it('Test destroy layer.', function () {
Expand Down

0 comments on commit 27b090e

Please sign in to comment.