diff --git a/UI/src/controller/laneModellingCollection.js b/UI/src/controller/laneModellingCollection.js
index 5cef0f0679..0a0fc03cf8 100644
--- a/UI/src/controller/laneModellingCollection.js
+++ b/UI/src/controller/laneModellingCollection.js
@@ -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 {
@@ -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);
};
diff --git a/UI/src/model/selectedLaneModelling.js b/UI/src/model/selectedLaneModelling.js
index 8f62907176..1193f055a6 100644
--- a/UI/src/model/selectedLaneModelling.js
+++ b/UI/src/model/selectedLaneModelling.js
@@ -74,28 +74,29 @@
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) {
@@ -103,15 +104,12 @@
});
}
- 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');
});
@@ -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;
});
};
@@ -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;
diff --git a/UI/src/view/linear_asset/laneModellingForm.js b/UI/src/view/linear_asset/laneModellingForm.js
index 22ea330750..c5605b186d 100644
--- a/UI/src/view/linear_asset/laneModellingForm.js
+++ b/UI/src/view/linear_asset/laneModellingForm.js
@@ -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('
');
- //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('
');
+ });
- body.find('.form').append('
');
}else{
renderFormElements(asset[0], isReadOnly, '', selectedAsset.setValue, selectedAsset.getValue, isDisabled, body);
diff --git a/UI/src/view/linear_asset/laneModellingLayer.js b/UI/src/view/linear_asset/laneModellingLayer.js
index 2d3f49bb9b..b008aa9f10 100644
--- a/UI/src/view/linear_asset/laneModellingLayer.js
+++ b/UI/src/view/linear_asset/laneModellingLayer.js
@@ -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) {
@@ -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);
@@ -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) {
@@ -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();
}
@@ -296,6 +340,7 @@
};
var drawSplitPoints = function (links) {
+
function findSplitPoints(links) {
var sortedPoints = _.flatMap(links, function (link) {
return link.points;
@@ -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);
@@ -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();
diff --git a/digiroad2-oracle/src/main/scala/fi/liikennevirasto/digiroad2/service/lane/LaneService.scala b/digiroad2-oracle/src/main/scala/fi/liikennevirasto/digiroad2/service/lane/LaneService.scala
index 1a10f9c473..b1d2f78620 100644
--- a/digiroad2-oracle/src/main/scala/fi/liikennevirasto/digiroad2/service/lane/LaneService.scala
+++ b/digiroad2-oracle/src/main/scala/fi/liikennevirasto/digiroad2/service/lane/LaneService.scala
@@ -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)
@@ -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
@@ -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
diff --git a/digiroad2-oracle/src/test/scala/fi/liikennevirasto/digiroad2/service/lane/LaneServiceSpec.scala b/digiroad2-oracle/src/test/scala/fi/liikennevirasto/digiroad2/service/lane/LaneServiceSpec.scala
index 061aefc58e..f318543e50 100644
--- a/digiroad2-oracle/src/test/scala/fi/liikennevirasto/digiroad2/service/lane/LaneServiceSpec.scala
+++ b/digiroad2-oracle/src/test/scala/fi/liikennevirasto/digiroad2/service/lane/LaneServiceSpec.scala
@@ -1397,4 +1397,234 @@ class LaneServiceSpec extends LaneTestSupporter {
laneTowardsTwoDigit.laneCode should equal(11)
laneAgainstTwoDigit.laneCode should equal(21)
}
+
+ test("Create three new split lanes") {
+ runWithRollback {
+ val mainLane1 = NewLane(0, 0, 500, 745, false, false, lanePropertiesValues1)
+ val mainLane1Id = ServiceWithDao.create(Seq(mainLane1), Set(100L), 1, usernameTest).head
+
+ val initialLanes = laneDao.fetchLanesByLinkIdsAndLaneCode(Seq(100L), Seq(1, 2), false)
+ initialLanes.size should be(1)
+
+ val createdMainLane = NewLane(mainLane1Id, 0, 500, 745, false, false, lanePropertiesValues1)
+
+ val subLane2SplitA = NewLane(0, 0, 150, 745, false, false, lanePropertiesValues2)
+ val subLane2SplitB = NewLane(0, 150, 350, 745, false, false, lanePropertiesValues2)
+ val subLane2SplitC = NewLane(0, 350, 500, 745, false, false, lanePropertiesValues2)
+
+ ServiceWithDao.processNewLanes(Set(createdMainLane, subLane2SplitA, subLane2SplitB, subLane2SplitC), Set(100L), 1, usernameTest, Seq())
+
+ val currentLanes = laneDao.fetchLanesByLinkIdsAndLaneCode(Seq(100L), Seq(1, 2), false)
+ currentLanes.size should be(4)
+
+ val lane1 = currentLanes.filter(_.id == mainLane1Id).head
+ lane1.id should be(mainLane1Id)
+ lane1.attributes.foreach { laneProp =>
+ lanePropertiesValues1.contains(laneProp) should be(true)
+ }
+
+ val splitLanes = currentLanes.filter(_.laneCode == 2).sortBy(_.startMeasure)
+ splitLanes.size should be(3)
+
+ splitLanes(0).startMeasure should be(0)
+ splitLanes(0).endMeasure should be(150)
+ splitLanes(1).startMeasure should be(150)
+ splitLanes(1).endMeasure should be(350)
+ splitLanes(2).startMeasure should be(350)
+ splitLanes(2).endMeasure should be(500)
+
+ splitLanes.foreach { lane =>
+ lane.attributes.foreach {
+ laneProp =>
+ lanePropertiesValues2.contains(laneProp) should be(true)
+ }
+ }
+
+ val expiredLanes = laneHistoryDao.fetchHistoryLanesByLinkIdsAndLaneCode(Seq(100L), Seq(2), true)
+ expiredLanes.size should be(0)
+ }
+ }
+
+ test("Split existing lane in three pieces") {
+ runWithRollback {
+ val mainLane1 = NewLane(0, 0, 500, 745, false, false, lanePropertiesValues1)
+ val subLane2 = NewLane(0, 0, 500, 745, false, false, lanePropertiesValues2)
+ val mainLane1Id = ServiceWithDao.create(Seq(mainLane1), Set(100L), 1, usernameTest).head
+ ServiceWithDao.create(Seq(subLane2), Set(100L), 1, usernameTest)
+
+ val initialLanes = laneDao.fetchLanesByLinkIdsAndLaneCode(Seq(100L), Seq(1, 2), false)
+ initialLanes.size should be(2)
+
+ val createdMainLane = NewLane(mainLane1Id, 0, 500, 745, false, false, lanePropertiesValues1)
+
+ val subLane2SplitA = NewLane(0, 0, 150, 745, false, false, lanePropertiesValues2)
+ val subLane2SplitB = NewLane(0, 150, 350, 745, false, false, lanePropertiesValues2)
+ val subLane2SplitC = NewLane(0, 350, 500, 745, false, false, lanePropertiesValues2)
+
+ ServiceWithDao.processNewLanes(Set(createdMainLane, subLane2SplitA, subLane2SplitB, subLane2SplitC), Set(100L), 1, usernameTest, Seq())
+
+ val currentLanes = laneDao.fetchLanesByLinkIdsAndLaneCode(Seq(100L), Seq(1, 2), false)
+ currentLanes.size should be(4)
+
+ val lane1 = currentLanes.filter(_.id == mainLane1Id).head
+ lane1.id should be(mainLane1Id)
+ lane1.attributes.foreach { laneProp =>
+ lanePropertiesValues1.contains(laneProp) should be(true)
+ }
+
+ val splitLanes = currentLanes.filter(_.laneCode == 2).sortBy(_.startMeasure)
+ splitLanes.size should be(3)
+
+ splitLanes(0).startMeasure should be(0)
+ splitLanes(0).endMeasure should be(150)
+ splitLanes(1).startMeasure should be(150)
+ splitLanes(1).endMeasure should be(350)
+ splitLanes(2).startMeasure should be(350)
+ splitLanes(2).endMeasure should be(500)
+
+ splitLanes.foreach { lane =>
+ lane.attributes.foreach {
+ laneProp =>
+ lanePropertiesValues2.contains(laneProp) should be(true)
+ }
+ }
+
+ val expiredLanes = laneHistoryDao.fetchHistoryLanesByLinkIdsAndLaneCode(Seq(100L), Seq(2), true)
+ expiredLanes.size should be(3)
+ }
+ }
+
+ test("Split lane in both ends, leaving the middle untouched") {
+ runWithRollback {
+ val mainLane1 = NewLane(0, 0, 500, 745, false, false, lanePropertiesValues1)
+ val subLane2SplitA = NewLane(0, 0, 150, 745, false, false, lanePropertiesValues2)
+ val subLane2SplitB = NewLane(0, 150, 350, 745, false, false, lanePropertiesValues2)
+ val subLane2SplitC = NewLane(0, 350, 500, 745, false, false, lanePropertiesValues2)
+ val laneIds = ServiceWithDao.create(Seq(mainLane1, subLane2SplitA, subLane2SplitB, subLane2SplitC), Set(100L), 1, usernameTest)
+
+ val initialLanes = laneDao.fetchLanesByLinkIdsAndLaneCode(Seq(100L), Seq(1, 2), false)
+ initialLanes.size should be(4)
+
+ val existingMainLane1 = NewLane(laneIds(0), 0, 500, 745, false, false, lanePropertiesValues1)
+ val newSubLane2SplitA1 = NewLane(0, 0, 50, 745, false, false, lanePropertiesValues2)
+ val newSubLane2SplitA2 = NewLane(0, 50, 150, 745, false, false, lanePropertiesValues2)
+ val existingSubLane2SplitB = NewLane(laneIds(2), 150, 350, 745, false, false, lanePropertiesValues2)
+ val newSubLane2SplitC1 = NewLane(0, 350, 450, 745, false, false, lanePropertiesValues2)
+ val newSubLane2SplitC2 = NewLane(0, 450, 500, 745, false, false, lanePropertiesValues2)
+
+ ServiceWithDao.processNewLanes(Set(existingMainLane1, newSubLane2SplitA1, newSubLane2SplitA2, existingSubLane2SplitB, newSubLane2SplitC1, newSubLane2SplitC2), Set(100L), 1, usernameTest, Seq())
+
+ val currentLanes = laneDao.fetchLanesByLinkIdsAndLaneCode(Seq(100L), Seq(1, 2), false)
+ currentLanes.size should be(6)
+
+ val expiredLanes = laneHistoryDao.fetchHistoryLanesByLinkIdsAndLaneCode(Seq(100L), Seq(2), true)
+ expiredLanes.size should be(4)
+ }
+ }
+
+ test("Change properties of several splits") {
+ runWithRollback {
+ val mainLane1 = NewLane(0, 0, 500, 745, false, false, lanePropertiesValues1)
+ val subLane2SplitA = NewLane(0, 0, 100, 745, false, false, lanePropertiesValues2)
+ val subLane2SplitB = NewLane(0, 100, 200, 745, false, false, lanePropertiesValues2)
+ val subLane2SplitC = NewLane(0, 200, 300, 745, false, false, lanePropertiesValues2)
+ val subLane2SplitD = NewLane(0, 300, 400, 745, false, false, lanePropertiesValues2)
+ val subLane2SplitE = NewLane(0, 400, 500, 745, false, false, lanePropertiesValues2)
+ val laneIds = ServiceWithDao.create(Seq(mainLane1, subLane2SplitA, subLane2SplitB, subLane2SplitC, subLane2SplitD, subLane2SplitE), Set(100L), 1, usernameTest)
+
+ val initialLanes = laneDao.fetchLanesByLinkIdsAndLaneCode(Seq(100L), Seq(1, 2), false)
+ initialLanes.size should be(6)
+
+ val newPropertyValues1 = Seq(LaneProperty("lane_code", Seq(LanePropertyValue(2))),
+ LaneProperty("lane_type", Seq(LanePropertyValue("5"))),
+ LaneProperty("start_date", Seq(LanePropertyValue(DateTime.now().toString("dd.MM.yyyy"))))
+ )
+
+ val newPropertyValues2 = Seq(LaneProperty("lane_code", Seq(LanePropertyValue(2))),
+ LaneProperty("lane_type", Seq(LanePropertyValue("13"))),
+ LaneProperty("start_date", Seq(LanePropertyValue(DateTime.now().toString("dd.MM.yyyy"))))
+ )
+
+ val createdMainLane = NewLane(laneIds(0), 0, 500, 745, false, false, lanePropertiesValues1)
+ val createdSubLane2SplitA = NewLane(laneIds(1), 0, 100, 745, false, false, lanePropertiesValues2)
+ val createdSubLane2SplitB = NewLane(laneIds(2), 100, 200, 745, false, false, newPropertyValues1)
+ val createdSubLane2SplitC = NewLane(laneIds(3), 200, 300, 745, false, false, newPropertyValues2)
+ val createdSubLane2SplitD = NewLane(laneIds(4), 300, 400, 745, false, false, lanePropertiesValues2)
+ val createdSubLane2SplitE = NewLane(laneIds(5), 400, 500, 745, false, false, newPropertyValues2)
+
+ ServiceWithDao.processNewLanes(Set(createdMainLane, createdSubLane2SplitA, createdSubLane2SplitB, createdSubLane2SplitC, createdSubLane2SplitD, createdSubLane2SplitE), Set(100L), 1, usernameTest, Seq())
+
+ val currentLanes = laneDao.fetchLanesByLinkIdsAndLaneCode(Seq(100L), Seq(1, 2), false)
+ currentLanes.size should be(6)
+
+ val splitLanes = currentLanes.filter(_.laneCode == 2).sortBy(_.startMeasure)
+
+ splitLanes(0).attributes.foreach {
+ laneProp =>
+ lanePropertiesValues2.contains(laneProp) should be(true)
+ }
+
+ splitLanes(1).attributes.foreach {
+ laneProp =>
+ newPropertyValues1.contains(laneProp) should be(true)
+ }
+
+ splitLanes(2).attributes.foreach {
+ laneProp =>
+ newPropertyValues2.contains(laneProp) should be(true)
+ }
+
+ splitLanes(3).attributes.foreach {
+ laneProp =>
+ lanePropertiesValues2.contains(laneProp) should be(true)
+ }
+
+ splitLanes(4).attributes.foreach {
+ laneProp =>
+ newPropertyValues2.contains(laneProp) should be(true)
+ }
+
+ val expiredLanes = laneHistoryDao.fetchHistoryLanesByLinkIdsAndLaneCode(Seq(100L), Seq(2), true)
+ expiredLanes.size should be(3)
+
+ }
+ }
+
+ test("Expire several split lanes") {
+ runWithRollback {
+ val mainLane1 = NewLane(0, 0, 500, 745, false, false, lanePropertiesValues1)
+ val subLane2SplitA = NewLane(0, 0, 100, 745, false, false, lanePropertiesValues2)
+ val subLane2SplitB = NewLane(0, 100, 200, 745, false, false, lanePropertiesValues2)
+ val subLane2SplitC = NewLane(0, 200, 300, 745, false, false, lanePropertiesValues2)
+ val subLane2SplitD = NewLane(0, 300, 400, 745, false, false, lanePropertiesValues2)
+ val subLane2SplitE = NewLane(0, 400, 500, 745, false, false, lanePropertiesValues2)
+ val laneIds = ServiceWithDao.create(Seq(mainLane1, subLane2SplitA, subLane2SplitB, subLane2SplitC, subLane2SplitD, subLane2SplitE), Set(100L), 1, usernameTest)
+
+ val initialLanes = laneDao.fetchLanesByLinkIdsAndLaneCode(Seq(100L), Seq(1, 2), false)
+ initialLanes.size should be(6)
+
+ val createdMainLane = NewLane(laneIds(0), 0, 500, 745, false, false, lanePropertiesValues1)
+ val createdSubLane2SplitA = NewLane(laneIds(1), 0, 100, 745, false, false, lanePropertiesValues2)
+ val createdSubLane2SplitB = NewLane(laneIds(2), 100, 200, 745, true, false, lanePropertiesValues2)
+ val createdSubLane2SplitC = NewLane(laneIds(3), 200, 300, 745, false, false, lanePropertiesValues2)
+ val createdSubLane2SplitD = NewLane(laneIds(4), 300, 400, 745, true, false, lanePropertiesValues2)
+ val createdSubLane2SplitE = NewLane(laneIds(5), 400, 500, 745, true, false, lanePropertiesValues2)
+
+ ServiceWithDao.processNewLanes(Set(createdMainLane, createdSubLane2SplitA, createdSubLane2SplitB, createdSubLane2SplitC, createdSubLane2SplitD, createdSubLane2SplitE), Set(100L), 1, usernameTest, Seq())
+
+ val currentLanes = laneDao.fetchLanesByLinkIdsAndLaneCode(Seq(100L), Seq(1, 2), false)
+ currentLanes.size should be(3)
+
+ val currentSplitLanes = currentLanes.filter(_.laneCode == 2).sortBy(_.startMeasure)
+ currentSplitLanes.size should be(2)
+
+ currentSplitLanes(0).startMeasure should be(0)
+ currentSplitLanes(0).endMeasure should be(100)
+ currentSplitLanes(1).startMeasure should be(200)
+ currentSplitLanes(1).endMeasure should be(300)
+
+ val expiredLanes = laneHistoryDao.fetchHistoryLanesByLinkIdsAndLaneCode(Seq(100L), Seq(2), true)
+ expiredLanes.size should be(3)
+ }
+ }
}