Skip to content

Commit

Permalink
Allow changing the feature.mouseover order.
Browse files Browse the repository at this point in the history
Add an event that allows changing the selection order for
feature.mouseover.  Also, add two common handlers for such ordering.

Specifically, the geo.event.feature.mouseover_order event is triggered
if the mouse is located on multiple elements of a feature.  The
`feature.mouseOverOrderHighestIndex` function will always sort these so
that the highest index element is on top.  The
`polygonFeature.mouseOverOrderClosestBorder` function will sort polygons
so that they are sorted based on how far the mouse is from their
borders, with the closest on top.  Note that, as always, the mouse must
be within the polygon for it to be considered.
  • Loading branch information
manthey committed Apr 11, 2018
1 parent 9e1317f commit 7e05b99
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 36 deletions.
7 changes: 7 additions & 0 deletions src/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,13 @@ geo_event.feature = {
* @event geo.event.feature.mouseover
*/
mouseover: 'geo_feature_mouseover',
/**
* The event contains the `feature`, the `previous` record of data elements
* that were under the mouse, and `over`, the new record of data elements that
* are unrder the mouse.
* @event geo.event.feature.mouseover.order
*/
mouseover_order: 'geo_feature_mouseover_order',
/**
* The event is the feature version of {@link geo.event.mouseout}.
* @event geo.event.feature.mouseout
Expand Down
34 changes: 34 additions & 0 deletions src/feature.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,22 @@ var feature = function (arg) {
if (!m_selectedFeatures.length && !over.index.length) {
return;
}

extra = over.extra || {};

// if we are over more than one item, trigger an event that is allowed to
// reorder the values in evt.over.index. Event handlers don't have to
// maintain evt.over.data. Handlers should not modify evt.over.extra or
// evt.previous.
if (over.index.length > 1) {
m_this.geoTrigger(geo_event.feature.mouseover_order, {
feature: this,
mouse: mouse,
previous: m_selectedFeatures,
over: over
});
}

// Get the index of the element that was previously on top
if (m_selectedFeatures.length) {
lastTop = m_selectedFeatures[m_selectedFeatures.length - 1];
Expand Down Expand Up @@ -733,6 +748,25 @@ var feature = function (arg) {
return this;
};

/**
* If the selectionAPI is on, then setting
* `this.geoOn(geo.event.feature.mouseover_order, this.mouseOverOrderHighestIndex)`
* will make it so that the mouseon events prefer the highest index feature.
*
* @param {geo.event} evt The event; this should be triggered from
* `geo.event.feature.mouseover_order`.
*/
this.mouseOverOrderHighestIndex = function (evt) {
// sort the found indices. The last one is the one "on top".
evt.over.index.sort();
// this isn't necessary, but ensures that other event handlers have
// consistent information
var data = evt.feature.data();
evt.over.index.forEach(function (di, idx) {
evt.over.found[idx] = data[di];
});
};

/**
* Initialize the class instance. Derived classes should implement this.
*
Expand Down
55 changes: 55 additions & 0 deletions src/polygonFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ var polygonFeature = function (arg) {
return coordinates;
}

/**
* Get the set of normalized polygon coordinates.
*
* @returns {object[]} An array of polygon positions. Each has `outer` and
* `inner` if it has any coordinates, or is undefined.
*/
this.polygonCoordinates = function () {
return m_coordinates;
};

/**
* Get the style for the stroke of the polygon. Since polygons can have
* holes, the number of stroke lines may not be the same as the number of
Expand Down Expand Up @@ -484,6 +494,51 @@ var polygonFeature = function (arg) {
return m_this;
};

/**
* If the selectionAPI is on, then setting
* `this.geoOn(geo.event.feature.mouseover_order, this.mouseOverOrderClosestBorder)`
* will make it so that the mouseon events prefer the polygon with the
* closet border, including hole edges.
*
* @param {geo.event} evt The event; this should be triggered from
* `geo.event.feature.mouseover_order`.
*/
this.mouseOverOrderClosestBorder = function (evt) {
var data = evt.feature.data(),
map = evt.feature.layer().map(),
pt = transform.transformCoordinates(map.ingcs(), evt.feature.gcs(), evt.mouse.geo),
coor = evt.feature.polygonCoordinates(),
dist = {};
evt.over.index.forEach(function (di, idx) {
var poly = coor[di], mindist;
poly.outer.forEach(function (line1, pidx) {
var line2 = poly.outer[(pidx + 1) % poly.outer.length];
var dist = util.distance2dToLineSquared(pt, line1, line2);
if (mindist === undefined || dist < mindist) {
mindist = dist;
}
});
poly.inner.forEach(function (inner) {
inner.forEach(function (line1, pidx) {
var line2 = inner[(pidx + 1) % inner.length];
var dist = util.distance2dToLineSquared(pt, line1, line2);
if (mindist === undefined || dist < mindist) {
mindist = dist;
}
});
});
dist[di] = mindist;
});
evt.over.index.sort(function (i1, i2) {
return dist[i1] - dist[i2];
}).reverse();
// this isn't necessary, but ensures that other event handlers have
// consistent information
evt.over.index.forEach(function (di, idx) {
evt.over.found[idx] = data[di];
});
};

/**
* Destroy.
*/
Expand Down
6 changes: 6 additions & 0 deletions tests/cases/feature.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,12 @@ describe('geo.feature', function () {
feat.updateStyleFromArray('radius', [11, 12, 13, 14], true);
expect(count).toBe(1);
});
it('mouseOverOrderHighestIndex', function () {
var evt = {over: {index: [3, 4, 2], found: []}, feature: feat};
expect(feat.mouseOverOrderHighestIndex(evt)).toBe(undefined);
expect(evt.over.index).toEqual([2, 3, 4]);
expect(evt.over.found.length).toBe(3);
});
});
describe('Check class accessors', function () {
var map, layer, feat;
Expand Down
134 changes: 98 additions & 36 deletions tests/cases/polygonFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,44 +147,106 @@ describe('geo.polygonFeature', function () {
});

describe('Public utility methods', function () {
describe('pointSearch', function () {
it('basic usage', function () {
mockVGLRenderer();
var map, layer, polygon, data, pt;
map = createMap();
layer = map.createLayer('feature', {renderer: 'vgl'});
polygon = geo.polygonFeature({layer: layer});
polygon._init();
data = testPolygons;
polygon.data(data);
pt = polygon.pointSearch({x: 5, y: 5});
expect(pt.index).toEqual([0]);
expect(pt.found.length).toBe(1);
expect(pt.found[0][0]).toEqual(data[0][0]);
pt = polygon.pointSearch({x: 21, y: 10});
expect(pt.index).toEqual([1]);
expect(pt.found.length).toBe(1);
pt = polygon.pointSearch({x: 30, y: 10});
expect(pt.index).toEqual([]);
expect(pt.found.length).toBe(0);
pt = polygon.pointSearch({x: 51, y: 10});
expect(pt.index).toEqual([2, 3]);
expect(pt.found.length).toBe(2);
pt = polygon.pointSearch({x: 57, y: 10});
expect(pt.index).toEqual([3]);
expect(pt.found.length).toBe(1);
/* If the inner hole extends past the outside, it doesn't make that
* point in the polygon */
pt = polygon.pointSearch({x: 60, y: 13});
expect(pt.index).toEqual([]);
expect(pt.found.length).toBe(0);
it('pointSearch', function () {
mockVGLRenderer();
var map, layer, polygon, data, pt;
map = createMap();
layer = map.createLayer('feature', {renderer: 'vgl'});
polygon = geo.polygonFeature({layer: layer});
polygon._init();
data = testPolygons;
polygon.data(data);
pt = polygon.pointSearch({x: 5, y: 5});
expect(pt.index).toEqual([0]);
expect(pt.found.length).toBe(1);
expect(pt.found[0][0]).toEqual(data[0][0]);
pt = polygon.pointSearch({x: 21, y: 10});
expect(pt.index).toEqual([1]);
expect(pt.found.length).toBe(1);
pt = polygon.pointSearch({x: 30, y: 10});
expect(pt.index).toEqual([]);
expect(pt.found.length).toBe(0);
pt = polygon.pointSearch({x: 51, y: 10});
expect(pt.index).toEqual([2, 3]);
expect(pt.found.length).toBe(2);
pt = polygon.pointSearch({x: 57, y: 10});
expect(pt.index).toEqual([3]);
expect(pt.found.length).toBe(1);
/* If the inner hole extends past the outside, it doesn't make that
* point in the polygon */
pt = polygon.pointSearch({x: 60, y: 13});
expect(pt.index).toEqual([]);
expect(pt.found.length).toBe(0);

// enable stroke and test very close, but outside, of an edge
polygon.style({stroke: true, strokeWidth: 20});
pt = polygon.pointSearch({x: 5, y: 2.499});
expect(pt.index).toEqual([0]);
restoreVGLRenderer();
// enable stroke and test very close, but outside, of an edge
polygon.style({stroke: true, strokeWidth: 20});
pt = polygon.pointSearch({x: 5, y: 2.499});
expect(pt.index).toEqual([0]);
restoreVGLRenderer();
});

it('polygonCoordinates', function () {
mockVGLRenderer();
var map, layer, polygon;
map = createMap();
layer = map.createLayer('feature', {renderer: 'vgl'});
polygon = geo.polygonFeature({layer: layer});
polygon._init();
polygon.data(testPolygons);
var result = polygon.polygonCoordinates();
expect(result.length).toEqual(testPolygons.length);
expect(result[0].outer.length).toBe(3);
expect(result[0].inner.length).toBe(0);
expect(result[1].outer.length).toBe(4);
expect(result[1].inner.length).toBe(1);
expect(result[1].inner[0].length).toBe(4);
restoreVGLRenderer();
});

it('mouseOverOrderClosestBorder', function () {
mockVGLRenderer();
var map, layer, polygon, data;
map = createMap();
layer = map.createLayer('feature', {renderer: 'vgl'});
polygon = geo.polygonFeature({layer: layer});
polygon._init();
// define some overlapping polygons for testing
data = [{
outer: [[29, 20], [35, 20], [32, 25]]
}, {
outer: [[29, 22], [35, 22], [32, 27]],
inner: [[[30, 22.6], [34, 22.6], [32, 26]]]
}, {
outer: [[22, 30], [27, 32], [24, 35], [22, 35]]
}, {
outer: [[20, 30], [25, 32], [22, 35], [20, 35]]
}];
polygon.data(data).position(function (vertex) {
return {x: vertex[0], y: vertex[1]};
});

var evt = {
over: {index: [2, 3], found: []},
feature: polygon,
mouse: {geo: {x: 22.7, y: 34}}
};
expect(polygon.mouseOverOrderClosestBorder(evt)).toBe(undefined);
expect(evt.over.index).toEqual([2, 3]);
evt.mouse.geo = {x: 22.2, y: 34};
polygon.mouseOverOrderClosestBorder(evt);
expect(evt.over.index).toEqual([3, 2]);
evt.over.index = [0, 1];
evt.mouse.geo = {x: 32, y: 22.2};
polygon.mouseOverOrderClosestBorder(evt);
expect(evt.over.index).toEqual([0, 1]);
evt.mouse.geo = {x: 30.5, y: 22.2};
polygon.mouseOverOrderClosestBorder(evt);
expect(evt.over.index).toEqual([1, 0]);
evt.mouse.geo = {x: 30.7, y: 22.5};
polygon.mouseOverOrderClosestBorder(evt);
expect(evt.over.index).toEqual([0, 1]);

restoreVGLRenderer();
});

describe('rdpSimplifyData', function () {
Expand Down

0 comments on commit 7e05b99

Please sign in to comment.