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

improved mirroring #484

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
235 changes: 111 additions & 124 deletions src/viewers/viewer/controls/Mirror.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,87 +2,59 @@ import {listen} from 'ol/events';
import EventType from 'ol/events/EventType';
import Control from 'ol/control/Control';
import {CLASS_UNSELECTABLE, CLASS_CONTROL } from 'ol/css';
import { rotate } from 'ol/coordinate';
import DragPan from 'ol/interaction/DragPan';

export class Mirror extends Control {
/**
* @constructor
* @param {ol.control.MirrorOptions=} opt_options options. (className, target)
* @param {ol.control.MirrorOptions=} opt_options options. (className, target, flipX, flipY)
*/
constructor(opt_options) {
var options = opt_options ? opt_options : {};
constructor(opt_options) {
const options = opt_options || {};

var element = document.createElement('div');
const element = document.createElement('div');
super({
element: element,
target: options.target
});

/**
* @type {string}
* @private
*/
this.class_name_ =
options.className === 'string' ? options.className : 'ol-flip';

/**
* @type {MapBrowserPointerEvent}
* @private
*/
this.ref_ = null

/**
* @type {View}
*/
this.view = null

/**
* @type {boolean}
* Sets default x inversion
*/
this.flipX =
typeof options.flipX == "boolean" ? options.flipX : false

/**
* @type {boolean}
* Sets default y inversion
*/
this.flipY =
typeof options.flipY == "boolean" ? options.flipY : false

var cssClasses =
this.class_name_ + ' ' + CLASS_UNSELECTABLE + ' ' +
CLASS_CONTROL;

// create button elements
this.class_name_ = typeof options.className === 'string' ? options.className : 'ol-flip';

this.ref_ = null; // reference for handling events

this.view = null; // Map view reference

this.initFlipX = typeof options.flipX === 'boolean' ? options.flipX : false;
this.initFlipY = typeof options.flipY === 'boolean' ? options.flipY : false;

const cssClasses = this.class_name_ + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL;

// Create button elements for flipping
element.className = cssClasses;
var buttonGroup = document.createElement('div');
buttonGroup.className = "btn-group btn-group-sm ol-flip-buttons";
const buttonGroup = document.createElement('div');
buttonGroup.className = 'btn-group btn-group-sm ol-flip-buttons';
buttonGroup.appendChild(this.addFlipButton(false));
buttonGroup.appendChild(this.addFlipButton(true));
element.appendChild(buttonGroup);

// need a map to finish intitialization
this.setMap_ = this.setMap
// Map setter override to initialize on map setup
this.setMap_ = this.setMap;
this.setMap = (map) => {
this.setMap_(map)
if (map != null) this.init()
}
this.setMap_(map);
if (map != null) this.init();
};
}

/**
* Adds both, flip vertical and horizontal buttons
* @param {boolean} flip_vertical the vertical flip button is added if true, otherwise horizontal
* Adds a flip button for horizontal or vertical flip
* @param {boolean} flip_vertical Adds vertical flip if true, else horizontal
* @private
*/
addFlipButton(flip_vertical) {
if (typeof flip_vertical !== 'boolean') flip_vertical = false;

var title = 'Flip ' + (flip_vertical ? 'vertical' : 'horizontal');
var element = document.createElement('button');
element.className =
this.class_name_ + (flip_vertical ? '-vertical glyphicon-resize-vertical' : '-horizontal glyphicon-resize-horizontal') +
" btn btn-default glyphicon ol-flip-button";
addFlipButton(flip_vertical) {
const title = `Flip ${flip_vertical ? 'vertical' : 'horizontal'}`;
const element = document.createElement('button');
element.className = this.class_name_ + (flip_vertical ? '-vertical glyphicon-resize-vertical' : '-horizontal glyphicon-resize-horizontal') +
' btn btn-default btn-primary glyphicon ol-flip-button';
element.setAttribute('type', 'button');
element.title = title;

Expand All @@ -91,82 +63,97 @@ export class Mirror extends Control {
return element;
}

init(){
this.view = this.getMap().getView()

this.getMap().getControls().getArray().forEach((control)=>{
if ('birds_eye_' in control){
this.birdseye = control
return
/**
* Initialization on map setup
*/
init() {
const map = this.getMap()
this.view = map.getView();

map.getControls().getArray().forEach((control) => {
if ('birds_eye_' in control) {
this.birdseye = control;
return;
}
})

this.view.constrainCenter_ = this.view.constrainCenter
this.view.constrainCenter = (center) => {
let curCenter = this.view.getCenter()
let rotation = this.view.getRotation()
// if there is rotation we need to undo it, mirror coords and then rerotate it
if (rotation != 0) {
rotate(center, Math.PI*2 - rotation)
rotate(curCenter, Math.PI*2 - rotation)
});
map.getInteractions().getArray().forEach((interaction) => {
if (interaction instanceof DragPan){
interaction.updateTrackedPointers__ = interaction.updateTrackedPointers_
interaction.updateTrackedPointers_ = (mapBrowserEvent) => {
if (this.view.get('flipX')) mapBrowserEvent.pointerEvent.clientX = mapBrowserEvent.pointerEvent.view.innerWidth - mapBrowserEvent.pointerEvent.clientX
if (this.view.get('flipY')) mapBrowserEvent.pointerEvent.clientY = mapBrowserEvent.pointerEvent.view.innerHeight - mapBrowserEvent.pointerEvent.clientY
return interaction.updateTrackedPointers__(mapBrowserEvent)
}
}
if (this.view.values_.flipX) center[0] = curCenter[0]-(center[0]-curCenter[0])
if (this.view.values_.flipY) center[1] = curCenter[1]-(center[1]-curCenter[1])
if (rotation != 0) rotate(center, rotation)
return this.view.constrainCenter_(center)
}

// override getEventPixel to account for mirroring
this.map_.getEventPixel = function (evt) {
const viewportPosition = this.viewport_.getBoundingClientRect();
const eventPosition =
//FIXME Are we really calling this with a TouchEvent anywhere?
'changedTouches' in evt
? /** @type {TouchEvent} */ (evt).changedTouches[0]
: /** @type {MouseEvent} */ (evt);

let x=eventPosition.clientX - viewportPosition.left
let y=eventPosition.clientY - viewportPosition.top
})

if (this.getView().flipX) x=viewportPosition.width-x
if (this.getView().flipY) y=viewportPosition.height-y
// Override getEventPixel to mirror the event based on flip state
map.getEventPixel = (evt) => {
const viewport = this.getMap().getViewport();
const viewportPosition = viewport.getBoundingClientRect();
const eventPosition = 'changedTouches' in evt ? evt.changedTouches[0] : evt;

let x = eventPosition.clientX - viewportPosition.left;
let y = eventPosition.clientY - viewportPosition.top;

// Apply flip transformations if necessary
if (this.view.get('flipX')) x = viewportPosition.width - x;
if (this.view.get('flipY')) y = viewportPosition.height - y;

return [x, y];
};

return [x,y]
}
if (this.flipX) this.flip(1)
if (this.flipY) this.flip(0)
if (this.initFlipX) this.flip(1);
if (this.initFlipY) this.flip(0);
}

// set desired transform and record in view
/**
* Apply or remove the flip transformation based on the axis
* @param {number} axis 0 for vertical flip (Y), 1 for horizontal flip (X)
*/
flip(axis) {
var viewport = this.getMap().getViewport().children[0] // (mirror just tiles)
let transform;
if (axis == 0) {
transform="scaleY(-1)"
this.view.setProperties({flipY:!(this.view.values_.flipY)})
} else {
transform="scaleX(-1)"
this.view.setProperties({flipX:!(this.view.values_.flipX)})
const viewport = this.getMap().getViewport().children[0]; // Mirror only the tiles
const selectionBox = this.getMap().getViewport().children[1]
const isY = (axis === 0);
const transformType = isY ? 'scaleY(-1)' : 'scaleX(-1)';
const viewProp = isY ? 'flipY' : 'flipX';

// Toggle the flip state in the view properties
const currentFlipState = this.view.get(viewProp);
this.view.set(viewProp, !currentFlipState);

// Update the viewport style transform
viewport.style.transform = currentFlipState ?
viewport.style.transform.replace(transformType, '') :
viewport.style.transform + ` ${transformType}`;

selectionBox.style.transform = currentFlipState ?
selectionBox.style.transform.replace(transformType, '') :
selectionBox.style.transform + ` ${transformType}`;
// Handle birds-eye control if present
if (this.birdseye && this.birdseye.controlDiv_) {
const birdseyeDiv = this.birdseye.controlDiv_;
birdseyeDiv.style.transform = currentFlipState ?
birdseyeDiv.style.transform.replace(transformType, '') :
birdseyeDiv.style.transform + ` ${transformType}`;
}

// if it is already mirrored remove mirror, otherwise add mirror
viewport.style.transform = viewport.style.transform.includes(transform) ?
viewport.style.transform.replace(transform, "") :
viewport.style.transform + transform
this.birdseye.controlDiv_.style.transform = this.birdseye.controlDiv_.style.transform.includes(transform) ?
this.birdseye.controlDiv_.style.transform.replace(transform, "") :
this.birdseye.controlDiv_.style.transform + transform
}


/**
* Handle button click for flipping the map
* @param {Event} event Button click event
* @private
*/
handleClick_(event) {
// 0 axis if vertical ( flip y over x axis )
// 1 axis if hortizontal ( flip x over y axis )
event.preventDefault();
var axis = event.target.className.indexOf("ol-flip-vertical") !== -1 ? 0 : 1;
event.target.style.backgroundColor = event.target.style.backgroundColor === 'silver' ? '' : 'silver'

this.flip(axis)
return true
}
const axis = event.target.className.includes('ol-flip-vertical') ? 0 : 1;
this.flip(axis);

return true;
}
}
export default Mirror

export default Mirror;