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) + } + } }