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

Fix selection rectangle and add selection-based zoom #582

Merged
merged 7 commits into from
Jun 14, 2016
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,33 @@ geo_event.brushend = 'geo_brushend';
//////////////////////////////////////////////////////////////////////////////
geo_event.brushstart = 'geo_brushstart';

//////////////////////////////////////////////////////////////////////////////
/**
* Triggered after a selection ends.
* The event object extends {@link geo.brushSelection}.
* @mixes geo.brushSelection
*/
//////////////////////////////////////////////////////////////////////////////
geo_event.select = 'geo_select';

//////////////////////////////////////////////////////////////////////////////
/**
* Triggered after a zoom selection ends.
* The event object extends {@link geo.brushSelection}.
* @mixes geo.brushSelection
*/
//////////////////////////////////////////////////////////////////////////////
geo_event.zoomselect = 'geo_zoomselect';

//////////////////////////////////////////////////////////////////////////////
/**
* Triggered after an unzoom selection ends.
* The event object extends {@link geo.brushSelection}.
* @mixes geo.brushSelection
*/
//////////////////////////////////////////////////////////////////////////////
geo_event.unzoomselect = 'geo_unzoomselect';

//////////////////////////////////////////////////////////////////////////////
/**
* Triggered before a map navigation animation begins. Set
Expand Down
145 changes: 110 additions & 35 deletions src/mapInteractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ var mapInteractor = function (args) {
m_queue,
$node,
m_selectionLayer = null,
m_selectionPlane = null,
m_selectionQuad,
m_paused = false,
m_clickMaybe = false,
m_callZoom = function () {};
Expand Down Expand Up @@ -68,19 +68,23 @@ var mapInteractor = function (args) {
zoomMoveButton: 'right',
zoomMoveModifiers: {},
rotateMoveButton: 'left',
rotateMoveModifiers: {'ctrl': true},
rotateMoveModifiers: {ctrl: true},
panWheelEnabled: false,
panWheelModifiers: {},
zoomWheelEnabled: true,
zoomWheelModifiers: {},
rotateWheelEnabled: true,
rotateWheelModifiers: {'ctrl': true},
rotateWheelModifiers: {ctrl: true},
wheelScaleX: 1,
wheelScaleY: 1,
zoomScale: 1,
rotateWheelScale: 6 * Math.PI / 180,
selectionButton: 'left',
selectionModifiers: {'shift': true},
selectionModifiers: {shift: true, ctrl: true},
zoomSelectionButton: 'left',
zoomSelectionModifiers: {shift: true},
unzoomSelectionButton: 'right',
unzoomSelectionModifiers: {shift: true},
momentum: {
enabled: true,
maxSpeed: 2.5,
Expand Down Expand Up @@ -202,7 +206,7 @@ var mapInteractor = function (args) {
// // a mousemove.
// click: {
// enabled: true | false,
// buttons: {'left': true, 'right': true, 'middle': true}
// buttons: {left: true, right: true, middle: true}
// duration: 0,
// cancelOnMove: true // cancels click if the mouse is moved before release
// }
Expand Down Expand Up @@ -406,7 +410,9 @@ var mapInteractor = function (args) {
if (m_options.panMoveButton === 'right' ||
m_options.zoomMoveButton === 'right' ||
m_options.rotateMoveButton === 'right' ||
m_options.selectionButton === 'right') {
m_options.selectionButton === 'right' ||
m_options.zoomSelectionButton === 'right' ||
m_options.unzoomSelectionButton === 'right') {
$node.on('contextmenu.geojs', function () { return false; });
}
return m_this;
Expand Down Expand Up @@ -561,27 +567,18 @@ var mapInteractor = function (args) {
};

// Get the gcs coordinates
gcs.upperLeft = map.displayToGcs(display.upperLeft);
gcs.lowerRight = map.displayToGcs(display.lowerRight);
gcs.upperRight = map.displayToGcs(display.upperRight);
gcs.lowerLeft = map.displayToGcs(display.lowerLeft);

m_selectionPlane.origin([
display.lowerLeft.x,
display.lowerLeft.y,
0
]);
m_selectionPlane.upperLeft([
display.upperLeft.x,
display.upperLeft.y,
0
]);
m_selectionPlane.lowerRight([
display.lowerRight.x,
display.lowerRight.y,
0
]);
m_selectionPlane.draw();
gcs.upperLeft = map.displayToGcs(display.upperLeft, null);
gcs.lowerRight = map.displayToGcs(display.lowerRight, null);
gcs.upperRight = map.displayToGcs(display.upperRight, null);
gcs.lowerLeft = map.displayToGcs(display.lowerLeft, null);

m_selectionQuad.data([{
ul: gcs.upperLeft,
ur: gcs.upperRight,
ll: gcs.lowerLeft,
lr: gcs.lowerRight
}]);
m_selectionQuad.draw();

return {
display: display,
Expand Down Expand Up @@ -653,6 +650,10 @@ var mapInteractor = function (args) {
action = 'rotate';
} else if (eventMatch(m_options.selectionButton, m_options.selectionModifiers)) {
action = 'select';
} else if (eventMatch(m_options.zoomSelectionButton, m_options.zoomSelectionModifiers)) {
action = 'zoomselect';
} else if (eventMatch(m_options.unzoomSelectionButton, m_options.unzoomSelectionModifiers)) {
action = 'unzoomselect';
}

// cancel transitions and momentum on click
Expand All @@ -677,7 +678,7 @@ var mapInteractor = function (args) {
delta: {x: 0, y: 0}
};

if (action === 'select') {
if (action === 'select' || action === 'zoomselect' || action === 'unzoomselect') {
// Make sure the old selection layer is gone.
if (m_selectionLayer) {
m_selectionLayer.clear();
Expand All @@ -686,10 +687,12 @@ var mapInteractor = function (args) {
}
// Create a feature layer and plane feature to show the selection bounds
m_selectionLayer = m_this.map().createLayer('feature', {renderer: 'd3'});
m_selectionPlane = m_selectionLayer.createFeature('plane');
m_selectionPlane.style({
screenCoordinates: true,
fillOpacity: function () { return 0.25; }

m_selectionQuad = m_selectionLayer.createFeature(
'quad', {gcs: m_this.map().gcs()});
m_selectionQuad.style({
opacity: 0.25,
color: {r: 0.3, g: 0.3, b: 0.3}
});
m_this.map().geoTrigger(geo_event.brushstart, m_this._getSelection());
}
Expand Down Expand Up @@ -798,7 +801,7 @@ var mapInteractor = function (args) {
cx = m_mouse.map.x - m_this.map().size().width / 2;
cy = m_mouse.map.y - m_this.map().size().height / 2;
m_this.map().rotation(m_state.origin.rotation + Math.atan2(cy, cx));
} else if (m_state.action === 'select') {
} else if (m_state.action === 'select' || m_state.action === 'zoomselect' || m_state.action === 'unzoomselect') {
// Get the bounds of the current selection
selectionObj = m_this._getSelection();
m_this.map().geoTrigger(geo_event.brush, selectionObj);
Expand Down Expand Up @@ -885,6 +888,71 @@ var mapInteractor = function (args) {
};
}

////////////////////////////////////////////////////////////////////////////
/**
* Based on the screen coodinates of a selection, zoom or unzoom and
* recenter.
*
* @private
* @param {string} action Either 'zoomselect' or 'unzoomselect'.
* @param {object} lowerLeft the x and y coordinates of the lower left corner
* of the zoom rectangle.
* @param {object} upperRight the x and y coordinates of the upper right
* corner of the zoom rectangle.
*/
////////////////////////////////////////////////////////////////////////////
this._zoomFromSelection = function (action, lowerLeft, upperRight) {
if (action !== 'zoomselect' && action !== 'unzoomselect') {
return;
}
if (lowerLeft.x === upperRight.x || lowerLeft.y === upperRight.y) {
return;
}
var zoom, center,
map = m_this.map(),
mapsize = map.size();
/* To arbitrarily handle rotation and projection, we center the map at the
* central coordinate of the selection and set the zoom level such that the
* four corners are just barely on the map. When unzooming (zooming out),
* we ensure that the previous view is centered in the selection but use
* the maximal size for the zoom factor. */
var scaling = {
x: Math.abs((upperRight.x - lowerLeft.x) / mapsize.width),
y: Math.abs((upperRight.y - lowerLeft.y) / mapsize.height)
};
if (action === 'zoomselect') {
center = map.displayToGcs({
x: (lowerLeft.x + upperRight.x) / 2,
y: (lowerLeft.y + upperRight.y) / 2
}, null);
zoom = map.zoom() - Math.log2(Math.max(scaling.x, scaling.y));
} else { /* unzoom */
/* To make the existing visible map entirely within the selection
* rectangle, this would be changed to Math.min instead of Math.max of
* the scaling factors. This felt wrong, though. */
zoom = map.zoom() + Math.log2(Math.max(scaling.x, scaling.y));
/* Record the current center. Later, this is panned to the center of the
* selection rectangle. */
center = map.center(undefined, null);
}
/* When discrete zoom is enable, always round down. We have to do this
* explicitly, as otherwise we may zoom too far and the selection will not
* be completely visible. */
if (map.discreteZoom()) {
zoom = Math.floor(zoom);
}
map.zoom(zoom);
if (action === 'zoomselect') {
map.center(center, null);
} else {
var newcenter = map.gcsToDisplay(center, null);
map.pan({
x: (lowerLeft.x + upperRight.x) / 2 - newcenter.x,
y: (lowerLeft.y + upperRight.y) / 2 - newcenter.y
});
}
};

////////////////////////////////////////////////////////////////////////////
/**
* Handle event when a mouse button is unpressed on the document.
Expand Down Expand Up @@ -912,15 +980,19 @@ var mapInteractor = function (args) {
evt.preventDefault();
}

if (m_state.action === 'select') {
if (m_state.action === 'select' || m_state.action === 'zoomselect' || m_state.action === 'unzoomselect') {
m_this._getMousePosition(evt);
selectionObj = m_this._getSelection();

m_selectionLayer.clear();
m_this.map().deleteLayer(m_selectionLayer);
m_selectionLayer = null;
m_selectionPlane = null;
m_selectionQuad = null;

m_this.map().geoTrigger(geo_event.brushend, selectionObj);
m_this.map().geoTrigger(geo_event[m_state.action], selectionObj);
m_this._zoomFromSelection(m_state.action, selectionObj.display.lowerLeft,
selectionObj.display.upperRight);
}

// reset the interactor state
Expand Down Expand Up @@ -1419,6 +1491,9 @@ var mapInteractor = function (args) {
}
}
);
if (type.indexOf('.geojs') >= 0) {
$(document).trigger(evt);
}
$node.trigger(evt);
};
this._connectEvents();
Expand Down
11 changes: 0 additions & 11 deletions src/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,6 @@ var renderer = function (arg) {
}
};

////////////////////////////////////////////////////////////////////////////
/**
* Get base layer that belongs to this renderer
*/
////////////////////////////////////////////////////////////////////////////
this.baseLayer = function () {
if (m_this.map()) {
return m_this.map().baseLayer();
}
};

////////////////////////////////////////////////////////////////////////////
/**
* Get/Set if renderer has been initialized
Expand Down
Loading