diff --git a/src/feature.js b/src/feature.js index d41c94bf23..42bdcf106c 100644 --- a/src/feature.js +++ b/src/feature.js @@ -139,12 +139,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]; @@ -164,6 +165,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 @@ -188,6 +190,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 @@ -216,6 +219,7 @@ var feature = function (arg) { m_this.geoTrigger(geo_event.feature.mouseon, { data: data[top], index: top, + extra: extra[top], mouse: mouse }, true); } @@ -230,7 +234,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; @@ -238,6 +243,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 d19b86c375..d64592550e 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,36 @@ 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) { + extra[quad.idx] = {basis: coordbasis}; + } } }); }); return { index: indices, - found: found + found: found, + extra: extra }; }; @@ -241,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 @@ -275,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) { diff --git a/src/util/init.js b/src/util/init.js index 8525790406..14815f9ff9 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 0722d25067..2ba3d1a27f 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); }); }); });