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

Ht 38 select task #51

Merged
merged 17 commits into from
Mar 15, 2017
Merged
Show file tree
Hide file tree
Changes from 8 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
124 changes: 115 additions & 9 deletions client/app/project/project.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,98 @@
*/
angular
.module('taskingManager')
.controller('projectController', ['$location', 'mapService', 'projectService', 'styleService', projectController]);
.controller('projectController', ['$scope', '$location', 'mapService', 'projectService', 'styleService', projectController]);

function projectController($location, mapService, projectService, styleService) {
function projectController($scope, $location, mapService, projectService, styleService) {
var vm = this;
vm.project = null;
vm.map = null;
vm.currentTab = '';
vm.mappingStep = '';

//selected task
vm.selectedTask = null;
vm.isSelectTaskMappable = false;

//interaction
var select = new ol.interaction.Select({
style: styleService.getSelectedStyleFunction
});

activate();

function activate() {
//TODO: Set up sidebar tabs
vm.currentTab = 'description';
vm.mappingStep = 'select';
mapService.createOSMMap('map');
vm.map = mapService.getOSMMap();

vm.map.addInteraction(select);
select.on('select', function (event) {
$scope.$apply(function () {
var feature = event.selected[0];
onTaskSelection(feature);
});
});

var id = $location.search().project;
initialiseProject(id);
//TODO: put the project metadata (description instructions on disebar tabs
//TODO: put the project metadata (description instructions on siedbar tabs
}

vm.selectRandomTask = function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add function comment

var task = getRandomMappableTask(vm.project.tasks);
if (task) {
//iterate layers to find task layer
var layers = vm.map.getLayers();
for (var i = 0; i < layers.getLength(); i++) {
if (layers.item(i).get('name') === 'tasks') {
var feature = layers.item(i).getSource().getFeatures().filter(function (feature) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of iterating over the layers, you could store the vector layer as a controller variable and reference it directly.

if (feature.get('taskId') === task.properties.taskId) {
// TODO the next few steps might be better done with event handling and dispatching to resuse
// through the listener code on the select interaction. Need to find a way to do that
select.getFeatures().clear();
select.getFeatures().push(feature);
onTaskSelection(feature);
var vPadding = vm.map.getSize()[1] * 0.3;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explain with a comment what this magic number is exactly

vm.map.getView().fit(feature.getGeometry().getExtent(), {padding: [vPadding, vPadding, vPadding, vPadding]});
}
});
break;
}
}
}
else {
vm.selectedTask = null;
vm.isSelectTaskMappable = false;
vm.mappingStep = 'none-available';
}
};

/**
* Get a project with using it's id
* clears the currently selected task. Clears down/resets the vm properties and clears the feature param in the select interaction object.
*/
function initialiseProject(id){
vm.clearCurrentSelection = function () {
vm.selectedTask = null;
vm.isSelectTaskMappable = false;
vm.currentTab = 'mapping';
vm.mappingStep = 'select';
select.getFeatures().clear();
};

/**
* Initilaise a project with using it's id
* @param id - id of the project to initialise
*/
function initialiseProject(id) {
var resultsPromise = projectService.getProject(id);
resultsPromise.then(function (data) {
//project returned successfully
vm.project = data;
addAoiToMap(vm.project.areaOfInterest);
addProjectTasksToMap(vm.project.tasks);
}, function(){
}, function () {
// project not returned successfully
// TODO - may want to handle error
});
Expand All @@ -47,11 +108,12 @@
* Adds project tasks to map as features from geojson
* @param tasks
*/
function addProjectTasksToMap(tasks){
function addProjectTasksToMap(tasks) {
//TODO: may want to refactor this into a service at some point so that it can be reused
var source = new ol.source.Vector();
var vector = new ol.layer.Vector({
source: source,
name: 'tasks',
style: styleService.getTaskStyleFunction
});
vm.map.addLayer(vector);
Expand All @@ -70,11 +132,12 @@
* Adds the aoi feature to the map
* @param aoi
*/
function addAoiToMap(aoi){
function addAoiToMap(aoi) {
//TODO: may want to refactor this into a service at some point so that it can be resused
var source = new ol.source.Vector();
var vector = new ol.layer.Vector({
source: source
source: source,
name: 'aoi'
});
vm.map.addLayer(vector);

Expand All @@ -86,5 +149,48 @@
});
source.addFeature(aoiFeatures);
}

/**
* returns a randomly selected mappable task from the passed in tasks JSON object
* @param tasks - the set of tasks from which to find a random task
* @returns task if one found, null if non available
*/
function getRandomMappableTask(tasks) {

// get all non locked ready tasks,
var candidates = []
var candidates = tasks.features.filter(function (item) {
if (!item.properties.taskLocked && item.properties.taskStatus === 'READY') return item;
});
// if no ready tasks, get non locked invalid tasks
if (candidates.length == 0) {
candidates = tasks.features.filter(function (item) {
if (!item.properties.taskLocked && item.properties.taskStatus === 'INVALIDATED') return item;
});
}

// if tasks were found, return a random task
if (candidates.length > 0) {
return candidates[Math.floor((Math.random() * (candidates.length - 1)))];
}

// if all else fails, return null
return null;
}

/**
* Sets up the model for currently selected feature
* @param feature
*/
function onTaskSelection(feature) {
var taskStatus = feature.get('taskStatus');
var taskLocked = feature.get('taskLocked')
vm.selectedTask = feature;
vm.isSelectTaskMappable = !taskLocked && (taskStatus === 'READY' || taskStatus === 'INVALIDATED');
vm.currentTab = 'mapping';
vm.mappingStep = 'view';
}


}
})();
104 changes: 97 additions & 7 deletions client/app/project/project.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,112 @@ <h1 class="section__title">
<div class="create-project">
<div class="sidebar">
<div class="button-group button-group--horizontal" role="group" aria-label="...">
<button ng-click="projectCtrl.currentTab = 'description'" class="button button--achromic" type="button" ng-class="projectCtrl.currentTab == 'description'? 'button--active':''">Description</button>
<button ng-click="projectCtrl.currentTab = 'instructions'" class="button button--achromic" type="button" ng-class="projectCtrl.currentTab == 'instructions'? 'button--active':''">Instructions</button>
<button ng-click="projectCtrl.currentTab = 'mapping'"class="button button--achromic" type="button" ng-class="projectCtrl.currentTab == 'mapping'? 'button--active':''">*Mapping*</button>
<button ng-click="projectCtrl.currentTab = 'description'" class="button button--achromic"
type="button" ng-class="projectCtrl.currentTab == 'description'? 'button--active':''">
Description
</button>
<button ng-click="projectCtrl.currentTab = 'instructions'" class="button button--achromic"
type="button" ng-class="projectCtrl.currentTab == 'instructions'? 'button--active':''">
Instructions
</button>
<button ng-click="projectCtrl.currentTab = 'mapping'" class="button button--achromic" type="button"
ng-class="projectCtrl.currentTab == 'mapping'? 'button--active':''">Mapping
</button>
</div>
<div ng-show="projectCtrl.currentTab === 'description'">
<h3>Description</h3>
<p>some text</p>
<button class="button button--secondary" ng-click="projectCtrl.currentTab = 'instructions'">Instructions</button>
<button class="button button--secondary" ng-click="projectCtrl.currentTab = 'instructions'">
Instructions
</button>
</div>
<div ng-show="projectCtrl.currentTab === 'instructions'">
<h3>Instructions</h3>
<button class="button button--secondary" ng-click="projectCtrl.currentTab = 'mapping'">Mapping</button>
<button class="button button--secondary" ng-click="projectCtrl.currentTab = 'mapping'">Mapping
</button>
</div>
<div ng-show="projectCtrl.currentTab === 'mapping'">
<h3>*Mapping*</h3>
<button class="button button--secondary">Start Mapping</button>
<div ng-show="projectCtrl.mappingStep === 'select'">
<h3>Choose a task</h3>
<p>There are 2 options for choosing a task:</p>
<p><strong>Option 1: </strong>Select a task by clicking on the map</p>
<p><strong>Option 2: </strong>Select a random task
<button ng-click="projectCtrl.selectRandomTask()" class="button button--secondary">
Select random
</button>
</p>
</div>
<div ng-show="projectCtrl.mappingStep === 'view'">
<h3>Task Status: <strong>{{ projectCtrl.selectedTask.get('taskStatus') }}</strong></h3>

<div ng-show="projectCtrl.isSelectTaskMappable" class="alert alert--success" role="alert">
<p>This task is available for mapping.</p>
</div>

<div ng-hide="projectCtrl.isSelectTaskMappable" class="alert alert--danger" role="alert">
<p>The selected task is not available for mapping.</p>
</div>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tidy up whitespaces

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

<div>
<button ng-show="projectCtrl.isSelectTaskMappable" class="button button--secondary">Start
Mapping
</button>
<button ng-click="projectCtrl.selectRandomTask()" class="button button--secondary">
Select another task
</button>
<button class="button button--secondary" ng-click="projectCtrl.clearCurrentSelection()">
Cancel
</button>

</div>
<hr/>
<div>
<div>
<h3>Advanced</h3>
<div>Advanced task information and editing options</div>
</div>
<div>
<h5>Details</h5>
<ul>
<li><strong>ID:</strong> #{{ projectCtrl.selectedTask.get('taskId') }}</li>
<li><strong>Status:</strong> {{ projectCtrl.selectedTask.get('taskStatus') }}
</li>
<li><strong>Locked:</strong> {{ projectCtrl.selectedTask.get('taskLocked') }}
</li>
<li><strong>Difficulty:</strong> TODO</li>
<li><strong>Assigned:</strong> TODO</li>
</ul>
<div>
<h5>History</h5>
<ul>
<li>TODO</li>
</ul>
</div>
<div>
<h5>Options</h5>
<label for="form-select-1">Editor</label>
<select class="form__control form__control--small" id="form-select-1">
<option selected="true">ID Editor</option>
<option>JOSM</option>
<option>Potlach 2</option>
<option>Walking papers</option>
<option>Field papers</option>
</select>
<button class="button button--primary-unbounded">Preview task in OSM ID Editor
</button>
<button class="button button--primary-unbounded">View OSM changesets</button>
<button class="button button--primary-unbounded">View changes in overpass turbo
</button>
</div>
</div>
</div>
</div>
<div ng-show="projectCtrl.mappingStep === 'none-available'">
<div class="alert alert--danger" role="alert">
<p>There are no tasks currently available for mapping.</p>
</div>
</div>

</div>
</div>
<div id="map" class="map-container"></div>
Expand Down
58 changes: 44 additions & 14 deletions client/app/services/style.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,38 @@

function styleService() {

// a default style is good practice!
var DEAFAULT_STYLE = new ol.style.Style({
fill: new ol.style.Fill({
color: [250,250,250,1]
}),
stroke: new ol.style.Stroke({
color: [220,220,220,1],
width: 1
})
});

// a javascript object literal can be used to cache
// previously created styles. Its very important for
// performance to cache styles.
var styleCache = {};

var FILL_COLOUR_READY = 'rgba(223,223,223,0.1)';//very light grey, 0.1 opacity
var FILL_COLOUR_INVALIDATED = 'rgba(255,0,0,0.4)';//red, 0.4 opacity
var FILL_COLOUR_DONE = 'rgba(255,165,0,0.4)';//orange, 0.4 opacity
var FILL_COLOUR_VALIDATED = 'rgba(0,128,0,0.4)';//green, 0.4 opacity
var FILL_COLOUR_LOCKED = 'rgba(30,144,255,0.4)';//blue, 0.4 opacity
var FILL_COLOUR_BADIMAGERY = 'rgba(0,0,0,0.4)';//black, 0.4 opacity

var STROKE_COLOUR = 'rgba(84,84,84,0.7)';//grey, 0.7 opacity
var STROKE_WIDTH = 1;

var STROKE_COLOUR_SELECTED = 'rgba(255,255,0,1)'//red, 1.0 opacity
var STROKE_WIDTH_SELECTED = 2;

var service = {
getTaskStyleFunction: getTaskStyleFunction,
getSelectedStyleFunction: getSelectedStyleFunction
};

return service;
Expand All @@ -26,16 +56,6 @@
*/
function getTaskStyleFunction(feature){

var FILL_COLOUR_READY = 'rgba(223,223,223,0.1)';//very light grey, 0.1 opacity
var FILL_COLOUR_INVALIDATED = 'rgba(255,0,0,0.4)';//red, 0.4 opacity
var FILL_COLOUR_DONE = 'rgba(255,165,0,0.4)';//orange, 0.4 opacity
var FILL_COLOUR_VALIDATED = 'rgba(0,128,0,0.4)';//green, 0.4 opacity
var FILL_COLOUR_LOCKED = 'rgba(30,144,255,0.4)';//blue, 0.4 opacity
var FILL_COLOUR_BADIMAGERY = 'rgba(0,0,0,0.4)';//black, 0.4 opacity

var STROKE_COLOUR = 'rgba(84,84,84,0.7)';//grey, 0.7 opacity
var STROKE_WIDTH = 1;

// Get the feature's properties that control styling
var status = feature.get('taskStatus');
var isLocked = feature.get('taskLocked');
Expand All @@ -61,19 +81,29 @@
else if (status === 'BADIMAGERY') {
fillColor = FILL_COLOUR_BADIMAGERY
}
else {
return DEAFAULT_STYLE;
}

// build an ol.style.Style to be returned using calculated settings
var style = new ol.style.Style({
return new ol.style.Style({
fill: new ol.style.Fill({
color: fillColor
}),
stroke: new ol.style.Stroke({
color: STROKE_COLOUR,
width: STROKE_WIDTH
})
})
});


}

return style;
function getSelectedStyleFunction(feature){
// get the base style for the feature and override it's outline only.
var baseStyle = getTaskStyleFunction(feature);
baseStyle.getStroke().setColor(STROKE_COLOUR_SELECTED);
baseStyle.getStroke().setWidth(STROKE_WIDTH_SELECTED);
return baseStyle;
}
}
})();
Loading