From 736016665f89fc1bd5de9d43e44f63309c116c60 Mon Sep 17 00:00:00 2001 From: David Manthey Date: Mon, 24 Oct 2016 13:59:21 -0400 Subject: [PATCH 1/2] Report where in a quad a point is located. When a point search is performed on a quad feature, in addition to determining if the point is in a quad, determine where it is within the quad. --- src/feature.js | 10 ++++++++-- src/quadFeature.js | 23 ++++++++++++++++++++--- src/util/init.js | 26 ++++++++++++++++++++++++++ tests/cases/quadFeature.js | 17 +++++++++++++++-- 4 files changed, 69 insertions(+), 7 deletions(-) diff --git a/src/feature.js b/src/feature.js index 6c76fa8de7..9c96636e03 100644 --- a/src/feature.js +++ b/src/feature.js @@ -138,12 +138,13 @@ var feature = function (arg) { var mouse = m_this.layer().map().interactor().mouse(), data = m_this.data(), over = m_this.pointSearch(mouse.geo), - newFeatures = [], oldFeatures = [], lastTop = -1, top = -1; + newFeatures = [], oldFeatures = [], lastTop = -1, top = -1, extra; // exit if we have no old or new found entries if (!m_selectedFeatures.length && !over.index.length) { return; } + extra = over.extra || {}; // Get the index of the element that was previously on top if (m_selectedFeatures.length) { lastTop = m_selectedFeatures[m_selectedFeatures.length - 1]; @@ -163,6 +164,7 @@ var feature = function (arg) { m_this.geoTrigger(geo_event.feature.mouseover, { data: data[i], index: i, + extra: extra[i], mouse: mouse, eventID: feature.eventID, top: idx === newFeatures.length - 1 @@ -187,6 +189,7 @@ var feature = function (arg) { m_this.geoTrigger(geo_event.feature.mousemove, { data: data[i], index: i, + extra: extra[i], mouse: mouse, eventID: feature.eventID, top: idx === over.index.length - 1 @@ -215,6 +218,7 @@ var feature = function (arg) { m_this.geoTrigger(geo_event.feature.mouseon, { data: data[top], index: top, + extra: extra[top], mouse: mouse }, true); } @@ -229,7 +233,8 @@ var feature = function (arg) { this._handleMouseclick = function (evt) { var mouse = m_this.layer().map().interactor().mouse(), data = m_this.data(), - over = m_this.pointSearch(mouse.geo); + over = m_this.pointSearch(mouse.geo), + extra = over.extra || {}; mouse.buttonsDown = evt.buttonsDown; feature.eventID += 1; @@ -237,6 +242,7 @@ var feature = function (arg) { m_this.geoTrigger(geo_event.feature.mouseclick, { data: data[i], index: i, + extra: extra[i], mouse: mouse, eventID: feature.eventID, top: idx === over.index.length - 1 diff --git a/src/quadFeature.js b/src/quadFeature.js index 1b7db080fe..59023840f0 100644 --- a/src/quadFeature.js +++ b/src/quadFeature.js @@ -138,10 +138,12 @@ var quadFeature = function (arg) { */ //////////////////////////////////////////////////////////////////////////// this.pointSearch = function (coordinate) { - var found = [], indices = [], data = m_this.data(), i, + var found = [], indices = [], extra = {}, poly1 = [{}, {}, {}, {}], poly2 = [{}, {}, {}, {}], + order1 = [0, 1, 2, 0], order2 = [1, 2, 3, 1], + data = m_this.data(), map = m_this.layer().map(), - order1 = [0, 1, 2, 0], order2 = [1, 2, 3, 1]; + i, coordbasis; coordinate = transform.transformCoordinates( map.ingcs(), map.gcs(), coordinate); if (!m_quads) { @@ -161,12 +163,27 @@ var quadFeature = function (arg) { util.pointInPolygon(coordinate, poly2)) { indices.push(quad.idx); found.push(data[quad.idx]); + coordbasis = util.pointTo2DTriangleBasis( + coordinate, poly1[0], poly1[1], poly1[2]); + if (!coordbasis || coordbasis.x + coordbasis.y > 1) { + coordbasis = util.pointTo2DTriangleBasis( + coordinate, poly2[2], poly2[1], poly2[0]); + if (coordbasis) { + coordbasis.x = 1 - coordbasis.x; + } + } else { + coordbasis.y = 1 - coordbasis.y; + } + if (coordbasis) { + extra[quad.idx] = {basis: coordbasis}; + } } }); }); return { index: indices, - found: found + found: found, + extra: extra }; }; diff --git a/src/util/init.js b/src/util/init.js index 23a4c2d7ff..c5f601aeaf 100644 --- a/src/util/init.js +++ b/src/util/init.js @@ -76,6 +76,32 @@ return inside; }, + /** + * Return a point in the basis of the triangle. If the point is located on + * a vertex of the triangle, it will be at vert0: (0, 0), vert1: (1, 0), + * vert2: (0, 1). If it is within the triangle, its coordinates will be + * 0 <= x <= 1, 0 <= y <= 1, x + y <= 1. + * + * @param {object} point: the point to convert. + * @param {object} vert0: vertex 0 of the triangle + * @param {object} vert1: vertex 1 (x direction) of the triangle + * @param {object} vert2: vertex 2 (y direction) of the triangle + * @returns {object} basisPoint: the point in the triangle basis, or + * undefined if the triangle is degenerate. + */ + pointTo2DTriangleBasis: function (point, vert0, vert1, vert2) { + var a = vert1.x - vert0.x, + b = vert2.x - vert0.x, + c = vert1.y - vert0.y, + d = vert2.y - vert0.y, + x = point.x - vert0.x, + y = point.y - vert0.y, + det = a * d - b * c; + if (det) { + return {x: (x * d - y * b) / det, y: (x * -c + y * a) / det}; + } + }, + /** * Returns true if the argument is a function. */ diff --git a/tests/cases/quadFeature.js b/tests/cases/quadFeature.js index fea8b43437..af19415092 100644 --- a/tests/cases/quadFeature.js +++ b/tests/cases/quadFeature.js @@ -196,7 +196,9 @@ describe('geo.quadFeature', function () { quad = geo.quadFeature({layer: layer}); quad._init(); data = [{ - ll: [-60, 10], ur: [-40, 30], image: preloadImage + ll: [-40, 30], ur: [-60, 10], image: preloadImage + }, { + ll: [-90, 10], ur: [-100, 10], image: preloadImage }, { ll: [-80, 10], lr: [-50, 10], ur: [-70, 30], image: preloadImage }]; @@ -205,12 +207,23 @@ describe('geo.quadFeature', function () { expect(pt.index).toEqual([0]); expect(pt.found.length).toBe(1); expect(pt.found[0].ll).toEqual(data[0].ll); + expect(pt.extra[0].basis.x).toBeCloseTo(0.25); + expect(pt.extra[0].basis.y).toBeCloseTo(0.047477); pt = quad.pointSearch({x: -55, y: 11}); - expect(pt.index).toEqual([0, 1]); + expect(pt.index).toEqual([0, 2]); expect(pt.found.length).toBe(2); + expect(pt.extra[0].basis.x).toBeCloseTo(0.75); + expect(pt.extra[0].basis.y).toBeCloseTo(0.047477); + expect(pt.extra[2].basis.x).toBeCloseTo(0.833333); + expect(pt.extra[2].basis.y).toBeCloseTo(0.952523); pt = quad.pointSearch({x: -35, y: 11}); expect(pt.index).toEqual([]); expect(pt.found.length).toBe(0); + /* not in a degenerate quad */ + pt = quad.pointSearch({x: -95, y: 10}); + expect(pt.index).toEqual([]); + expect(pt.found.length).toBe(0); + expect(pt.extra[1]).toBe(undefined); }); }); }); From 1e7dec0db4b95ccd021b49334fd01b15128584f9 Mon Sep 17 00:00:00 2001 From: David Manthey Date: Thu, 27 Oct 2016 16:35:50 -0400 Subject: [PATCH 2/2] Add and improve comments. --- src/quadFeature.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/quadFeature.js b/src/quadFeature.js index 59023840f0..16cb9585ef 100644 --- a/src/quadFeature.js +++ b/src/quadFeature.js @@ -163,15 +163,24 @@ var quadFeature = function (arg) { util.pointInPolygon(coordinate, poly2)) { indices.push(quad.idx); found.push(data[quad.idx]); + /* If a point is in the quad (based on pointInPolygon, above), check + * where in the quad it is located. We want to output coordinates + * where the upper-left is (0, 0) and the lower-right is (1, 1). */ coordbasis = util.pointTo2DTriangleBasis( coordinate, poly1[0], poly1[1], poly1[2]); if (!coordbasis || coordbasis.x + coordbasis.y > 1) { coordbasis = util.pointTo2DTriangleBasis( coordinate, poly2[2], poly2[1], poly2[0]); if (coordbasis) { + /* In the second triangle, (0, 0) is upper-right, (1, 0) is + * upper-left, and (0, 1) is lower-right. Invert x to get to + * the desired output coordinates. */ coordbasis.x = 1 - coordbasis.x; } } else { + /* In the first triangle, (0, 0) is lower-left, (1, 0) is lower- + * right, and (0, 1) is upper-left. Invert y to get to the + * desired output coordinates. */ coordbasis.y = 1 - coordbasis.y; } if (coordbasis) { @@ -258,7 +267,7 @@ var quadFeature = function (arg) { /** * Convert the current data set to a pair of arrays, one of quads that are - * solid color and one of qudas that have an image. All quads are objects + * solid color and one of quads that have an image. All quads are objects * with pos (a 12 value array containing 4 three-dimensional position * coordinates), and opacity. Color quads also have a color. Image quads * may have an image element, if the image is loaded. If it isn't, this @@ -292,7 +301,7 @@ var quadFeature = function (arg) { clrQuads = [], imgQuads = [], origin = [0, 0, 0], origindiag2, diag2; /* Keep track of images that we are using. This prevents creating - * additional Image elemnts for repeated urls. */ + * additional Image elements for repeated urls. */ m_this._objectListStart(m_images); $.each(data, function (i, d) { if (d._cachedQuad) {