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

Droth 3109 lane split problems #1955

Closed
wants to merge 8 commits into from
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 @@ -579,7 +579,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 @@ -883,14 +895,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 @@ -916,29 +928,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