Skip to content

Commit

Permalink
Merge pull request #1958 from finnishtransportagency/DROTH-3109_lane_…
Browse files Browse the repository at this point in the history
…split_problems_branched_from_master

Droth 3109 lane split problems branched from master
  • Loading branch information
SWJPTulijoki authored Apr 28, 2022
2 parents be5c4ac + 9df78c7 commit 0a93df2
Show file tree
Hide file tree
Showing 6 changed files with 364 additions and 74 deletions.
5 changes: 1 addition & 4 deletions UI/src/controller/laneModellingCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
right.points = split.secondSplitVertices;
right.startMeasure = split.splitMeasure;

if (self.calculateMeasure(left) < self.calculateMeasure(right)) {
if (left.startMeasure < right.startMeasure) {
self.splitLinearAssets.created = left;
self.splitLinearAssets.existing = right;
} else {
Expand All @@ -53,9 +53,6 @@
self.splitLinearAssets.created.id = 0;
self.splitLinearAssets.existing.id = 0;

self.splitLinearAssets.created.marker = 'A';
self.splitLinearAssets.existing.marker = 'B';

self.dirty = true;
callback(self.splitLinearAssets);
};
Expand Down
57 changes: 31 additions & 26 deletions UI/src/model/selectedLaneModelling.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,44 +74,42 @@
return numberOfLanesByLaneCode[key] > 1;
});

var lanesSortedByLaneCode = _.sortBy(lanes, getLaneCodeValue);

var duplicateLaneCounter = 0;
return _.map(lanesSortedByLaneCode, function (lane) {
if(_.includes(laneCodesToPutMarkers, getLaneCodeValue(lane).toString())) {
if (duplicateLaneCounter === 0){
lane.marker = 'A';
duplicateLaneCounter++;
}else{
lane.marker = 'B';
duplicateLaneCounter--;
var lanesSortedByEndMeasure = _.sortBy(lanes, function(lane) {
return lane.endMeasure;
});

_.forEach(laneCodesToPutMarkers, function (laneCode) {
var characterCounterForLaneMarker = 0;
for (var i = 0; i < lanesSortedByEndMeasure.length; i++) {
if (laneCode == getLaneCodeValue(lanesSortedByEndMeasure[i]).toString()) {
//The integer value of 'A' is 65, so every increment in the counter gives the next letter of the alphabet.
lanesSortedByEndMeasure[i].marker = String.fromCharCode(characterCounterForLaneMarker + 65);
characterCounterForLaneMarker += 1;
}
}
return lane;
});

return lanesSortedByEndMeasure;
};

//Outer lanes that are expired are to be considered, the other are updates so we need to take those out
//Here a outer lane is a lane with lane code that existed in the original but not in the modified configuration
function omitIrrelevantExpiredLanes() {
var lanesToBeRemovedFromExpire = _.filter(assetsToBeExpired, function (lane) {
return !_.isUndefined(self.getLane(getLaneCodeValue(lane)));
return !self.isOuterLane(getLaneCodeValue(lane));
});

_.forEach(lanesToBeRemovedFromExpire, function (lane) {
_.remove(assetsToBeExpired, {'id': lane.id});
});
}

self.splitLinearAsset = function(laneNumber, split) {
collection.splitLinearAsset(self.getLane(laneNumber), split, function(splitLinearAssets) {
if (self.getLane(laneNumber).id === 0) {
self.removeLane(laneNumber);
} else {
self.expireLane(laneNumber);
}

self.splitLinearAsset = function(laneNumber, split, laneMarker) {
collection.splitLinearAsset(self.getLane(laneNumber, laneMarker), split, function(splitLinearAssets) {
var laneIndex = getLaneIndex(laneNumber, laneMarker);
self.selection.splice(laneIndex,1);
self.selection.push(splitLinearAssets.created, splitLinearAssets.existing);
self.selection = giveSplitMarkers(self.selection);
self.dirty = true;
eventbus.trigger('laneModellingForm: reload');
});
Expand Down Expand Up @@ -198,18 +196,25 @@

self.lanesCutAreEqual = function() {
var laneNumbers = _.map(self.selection, getLaneCodeValue);
var cuttedLaneNumbers = _.transform(_.countBy(laneNumbers), function(result, count, value) {
var cutLaneNumbers = _.transform(_.countBy(laneNumbers), function(result, count, value) {
if (count > 1) result.push(value);
}, []);

return _.some(cuttedLaneNumbers, function (laneNumber){
return _.some(cutLaneNumbers, function (laneNumber){
var lanes = _.filter(self.selection, function (lane){
return _.find(lane.properties, function (property) {
return property.publicId == "lane_code" && _.head(property.values).value == laneNumber;
});
});

return _.isEqual(lanes[0].properties, lanes[1].properties);
var sortedLanes = _.sortBy(lanes, function (lane) {
return lane.endMeasure;
});
for (var i = 1; i < sortedLanes.length; i++) {
if (_.isEqual(sortedLanes[i - 1].properties, sortedLanes[i].properties)) {
return true;
}
}
return false;
});
};

Expand Down Expand Up @@ -382,7 +387,7 @@

//expiredLane could be modified by the user so we need to fetch the original
var originalExpiredLane = _.find(lanesFetched, {'id': expiredLane.id});
if (linksSelected.length > 1) {
if (linksSelected.length > 1 && _.isUndefined(marker)) {
var expiredGroup = collection.getGroup(originalExpiredLane);
expiredGroup.forEach(function (lane) {
lane.isExpired = true;
Expand Down
21 changes: 9 additions & 12 deletions UI/src/view/linear_asset/laneModellingForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -443,23 +443,20 @@
});
});

asset = _.sortBy(asset, function (lane) {
return lane.marker;
});

var body = createBodyElement(selectedAsset.getCurrentLane());

if(selectedAsset.isSplit()) {
//Render form A
renderFormElements(_.find(asset,{'marker': 'A'}), isReadOnly, 'A', selectedAsset.setValue, selectedAsset.getValue, false, body);

if(!isReadOnly)
renderExpireAndDeleteButtonsElement(selectedAsset, body, 'A');

body.find('.form').append('<hr class="form-break">');
//Render form B
renderFormElements(_.find(asset,{'marker': 'B'}), isReadOnly, 'B', selectedAsset.setValue, selectedAsset.getValue, false, body);

if(!isReadOnly)
renderExpireAndDeleteButtonsElement(selectedAsset, body, 'B');
_.forEach(asset, function (lane) {
renderFormElements(lane, isReadOnly, lane.marker, selectedAsset.setValue, selectedAsset.getValue, false, body);
if(!isReadOnly) renderExpireAndDeleteButtonsElement(selectedAsset, body, lane.marker);
body.find('.form').append('<hr class="form-break">');
});

body.find('.form').append('<hr class="form-break">');
}else{
renderFormElements(asset[0], isReadOnly, '', selectedAsset.setValue, selectedAsset.getValue, isDisabled, body);

Expand Down
85 changes: 65 additions & 20 deletions UI/src/view/linear_asset/laneModellingLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@

var LinearAssetCutter = function(eventListener, vectorLayer) {
var scissorFeatures = [];
var CUT_THRESHOLD = 20;
//Max euclidean distance in geometry points allowed between mouse point and the closest linear asset point (the planned cut point).
var CUT_THRESHOLD = 5;
var vectorSource = vectorLayer.getSource();

var moveTo = function(x, y) {
Expand Down Expand Up @@ -81,8 +82,17 @@
return property.publicId === "lane_code";
}).values).value;

return !selectedLane.isOuterLane(laneNumber) || laneNumber == 1 ||
!_.isUndefined(properties.marker) || properties.selectedLinks.length > 1 || selectedLane.isAddByRoadAddress();
var uniqueSelectedRoadLinkIds = _.uniq(_.map(properties.selectedLinks, function (selectedLink) {
return selectedLink.linkId;
}));
if(_.isUndefined(uniqueSelectedRoadLinkIds)) {
return true;
}

var SelectedLaneCannotBeCut = !selectedLane.isOuterLane(laneNumber) || laneNumber == 1 || laneNumber != selectedLane.getCurrentLaneNumber() ||
uniqueSelectedRoadLinkIds.length > 1 || selectedLane.isAddByRoadAddress();

return SelectedLaneCannotBeCut;
})
.map(function(feature) {
var closestP = feature.getGeometry().getClosestPoint(point);
Expand All @@ -100,6 +110,44 @@
.value();
};

var getLanePieceToCut = function (point) {

var minSquaredDistanceBetweenPointAndLane = function (lane, point) {
var min_x_dist = Math.abs(lane.points[0].x - point[0]);
var min_y_dist = Math.abs(lane.points[0].y - point[1]);
for(var i=1; i < lane.points.length; i++) {
min_x_dist = Math.min(min_x_dist, Math.abs(lane.points[i].x - point[0]));
min_y_dist = Math.min(min_y_dist, Math.abs(lane.points[i].y - point[1]));
}
return Math.pow(min_x_dist, 2) + Math.pow(min_y_dist, 2);
};

var selected = selectedLane.get();

selected = _.reject(selected, function (lane) {
var laneNumber = _.head(_.find(lane.properties, function(property){
return property.publicId === "lane_code";
}).values).value;

return !selectedLane.isOuterLane(laneNumber) || laneNumber == 1 ||
laneNumber != selectedLane.getCurrentLaneNumber() || selectedLane.isAddByRoadAddress();
});

selected = _.sortBy(selected, function (lane) {
return minSquaredDistanceBetweenPointAndLane(lane, point);
});

var cutLanes = _.reject(selected, function (lane) {
return _.isUndefined(lane.marker);
});

if (_.isEmpty(cutLanes)) {
return _.head(selected);
}

return _.head(cutLanes);
};

this.updateByPosition = function(mousePoint) {
var closestLinearAssetLink = findNearestLinearAssetLink(mousePoint);
if (closestLinearAssetLink) {
Expand Down Expand Up @@ -128,18 +176,14 @@
return _.merge({ splitMeasure: splitMeasure }, splitVertices);
};

var nearest = findNearestLinearAssetLink([mousePoint.x, mousePoint.y]);

if (_.isUndefined(nearest) || !isWithinCutThreshold(nearest.distance)) {
return;
}
var nearestLinearAsset = getLanePieceToCut([mousePoint.x, mousePoint.y]);
if (_.isUndefined(nearestLinearAsset)) return;

var nearestLinearAsset = nearest.feature.getProperties();
if(authorizationPolicy.formEditModeAccess(nearestLinearAsset)) {
var splitProperties = calculateSplitProperties(laneUtils.offsetByLaneNumber(nearestLinearAsset, false, true), mousePoint);
var splitProperties = calculateSplitProperties(nearestLinearAsset, mousePoint);
selectedLane.splitLinearAsset(_.head(_.find(nearestLinearAsset.properties, function(property){
return property.publicId === "lane_code";
}).values).value, splitProperties);
}).values).value, splitProperties, nearestLinearAsset.marker);

remove();
}
Expand Down Expand Up @@ -296,6 +340,7 @@
};

var drawSplitPoints = function (links) {

function findSplitPoints(links) {
var sortedPoints = _.flatMap(links, function (link) {
return link.points;
Expand Down Expand Up @@ -381,7 +426,7 @@
var linearAssets = _.flatten(linearAssetChains);
var allButSelected = _.filter(linearAssets, function(asset){ return !_.some(selectedLane.get(), function(selectedAsset){
return selectedAsset.linkId === asset.linkId && selectedAsset.sideCode == asset.sideCode &&
selectedAsset.startMeasure === asset.startMeasure && selectedAsset.endMeasure === asset.endMeasure; }) ;
selectedAsset.startMeasure === asset.startMeasure && selectedAsset.endMeasure === asset.endMeasure; }) ;
});
me.vectorSource.addFeatures(style.renderFeatures(allButSelected));
var oneWaySignsDraft = getOneWaySigns(allButSelected);
Expand Down Expand Up @@ -430,17 +475,17 @@
var selectedFeatures = style.renderFeatures(linearAssets, laneNumber);

if (assetLabel) {
var currentFeatures = _.filter(me.vectorSource.getFeatures(), function (layerFeature) {
return _.some(selectedFeatures, function (selectedFeature) {
return me.geometryAndValuesEqual(selectedFeature.values_, layerFeature.values_);
});
var currentFeatures = _.filter(me.vectorSource.getFeatures(), function (layerFeature) {
return _.some(selectedFeatures, function (selectedFeature) {
return me.geometryAndValuesEqual(selectedFeature.values_, layerFeature.values_);
});
});

_.each(currentFeatures, me.removeFeature);
_.each(currentFeatures, me.removeFeature);

selectedFeatures = selectedFeatures.concat(assetLabel.renderFeaturesByLinearAssets(extractMiddleLinksOfChains([_.map(selectedFeatures, function (feature) {
return feature.getProperties();
})]), me.uiState.zoomLevel));
selectedFeatures = selectedFeatures.concat(assetLabel.renderFeaturesByLinearAssets(extractMiddleLinksOfChains([_.map(selectedFeatures, function (feature) {
return feature.getProperties();
})]), me.uiState.zoomLevel));
}

removeOldAssetFeatures();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,19 @@ trait LaneOperations {
val oldLane = allExistingLanes.find(laneAux => laneAux.laneCode == lane.laneCode && laneAux.linkId == linkId)
.getOrElse(throw new InvalidParameterException(s"LinkId: $linkId dont have laneCode: ${lane.laneCode} for update!"))

if (linkIds.size == 1 &&
val isExactlyMatchingSingleLinkLane = (lane: PersistedLane) => linkIds.size == 1 &&
lane.startMeasure == laneToUpdate.startMeasure && lane.startMeasure == laneToUpdate.startMeasure && lane.laneCode == laneToUpdateCode

if (isExactlyMatchingSingleLinkLane(lane)) {
var newLaneID = lane.id
if (isSomePropertyDifferent(lane, laneToUpdate.properties)) {
val persistedLaneToUpdate = PersistedLane(lane.id, linkId, sideCode, laneToUpdateCode, lane.municipalityCode,
lane.startMeasure, lane.endMeasure, Some(username), None, None, None, None, None, false, 0, None, laneToUpdate.properties)
moveToHistory(lane.id, None, false, false, username)
newLaneID = dao.updateEntryLane(persistedLaneToUpdate, username)
}
newLaneID
} else if (linkIds.size == 1 &&
(oldLane.startMeasure != laneToUpdate.startMeasure || oldLane.endMeasure != laneToUpdate.endMeasure)) {
val newLaneID = create(Seq(laneToUpdate), Set(linkId), sideCode, username)
moveToHistory(oldLane.id, Some(newLaneID.head), true, true, username)
Expand Down Expand Up @@ -943,14 +955,14 @@ trait LaneOperations {
}

//Get multiple lanes in one link
val resultWithMultiLanesInLink = newLanes.filter(_.isExpired != true).foldLeft(resultWithDeleteActions) {
val resultWithMultiLanesInLink = newLanes.filter(_.id == 0).foldLeft(resultWithDeleteActions) {
(result, newLane) =>
val newLaneCode: Int = getPropertyValue(newLane.properties, "lane_code").toString.toInt

val numberOfOldLanesByCode = allExistingLanes.count(_.laneCode == newLaneCode)
val numberOfFutureLanesByCode = newLanes.filter(_.isExpired != true).count { newLane => getLaneCode(newLane).toInt == newLaneCode }

if ((numberOfFutureLanesByCode == 2 && numberOfOldLanesByCode >= 1) || (numberOfFutureLanesByCode == 1 && numberOfOldLanesByCode == 2))
if ((numberOfFutureLanesByCode >= 2 && numberOfOldLanesByCode >= 1) || (numberOfFutureLanesByCode < numberOfOldLanesByCode))
result.copy(multiLanesOnLink = Set(newLane) ++ result.multiLanesOnLink)
else
result
Expand All @@ -976,29 +988,33 @@ trait LaneOperations {
}

def createMultiLanesOnLink(updateNewLanes: Seq[NewLane], linkIds: Set[Long], sideCode: Int, username: String): Seq[Long] = {
// Get all lane codes from lanes to update
val laneCodesToModify = updateNewLanes.map { newLane => getLaneCode(newLane).toInt }

//Fetch from db the existing lanes
val oldLanes = dao.fetchLanesByLinkIdsAndLaneCode(linkIds.toSeq, laneCodesToModify)

val laneCodesToModify = updateNewLanes.map { newLane => getLaneCode(newLane).toInt }
val oldLanes = dao.fetchLanesByLinkIdsAndLaneCode(linkIds.toSeq, laneCodesToModify).filter(lane => lane.sideCode == sideCode)
val newLanesByLaneCode = updateNewLanes.groupBy(il => getLaneCode(il).toInt)

//By lane check if exist something to modify
newLanesByLaneCode.flatMap { case (laneCode, lanesToUpdate) =>
val oldLanesByCode = oldLanes.filter(_.laneCode == laneCode)


if (lanesToUpdate.size == 2 && oldLanesByCode.size == 1) {
//When its to create two lanes in one link
if (lanesToUpdate.size >= 2) {
//When one or more lanes are cut to smaller pieces
val newLanesIDs = lanesToUpdate.map { lane =>
create(Seq(lane), linkIds, sideCode, username).head
}

def isWithinRangeToExpire(newLanes: Seq[NewLane], oldLane: PersistedLane): Boolean = {
newLanes.filter(newLane => newLane.startMeasure == oldLane.startMeasure || newLane.endMeasure == oldLane.endMeasure).size == 2
}

newLanesIDs.foreach { newLane =>
moveToHistory(oldLanesByCode.head.id, Some(newLane), true, false, username)
}
dao.deleteEntryLane(oldLanesByCode.head.id)
oldLanes.foreach {oldLane =>
if (isWithinRangeToExpire(lanesToUpdate, oldLane)) {
dao.deleteEntryLane(oldLane.id)
}
}

newLanesIDs

Expand Down
Loading

0 comments on commit 0a93df2

Please sign in to comment.