public EquipmentServiceQuery(IRouteNetworkState routeNetwork)
        {
            Description = "GraphQL API for querying OpenFTTH data";

            Field <StringGraphType>("apiVersion", resolve: context => VersionInfo.VersionString());

            Field <ListGraphType <RouteNodeType> >(
                "routeNodes",
                resolve: context =>
            {
                return(routeNetwork.GetAllRouteNodes());
            }
                );

            Field <RouteNodeType>(
                "routeNode",
                arguments: new QueryArguments(new QueryArgument <NonNullGraphType <IdGraphType> > {
                Name = "Id"
            }),
                resolve: context =>
            {
                Guid id;
                if (!Guid.TryParse(context.GetArgument <string>("id"), out id))
                {
                    context.Errors.Add(new ExecutionError("Wrong value for guid"));
                    return(null);
                }

                return(routeNetwork.GetRouteNodeInfo(id));
            }
                );


            Field <ListGraphType <RouteSegmentType> >(
                "routeSegments",
                resolve: context => {
                return(routeNetwork.GetAllRouteSegments());
            }
                );

            Field <RouteSegmentType>(
                "routeSegment",
                arguments: new QueryArguments(new QueryArgument <NonNullGraphType <IdGraphType> > {
                Name = "Id"
            }),
                resolve: context =>
            {
                Guid id;
                if (!Guid.TryParse(context.GetArgument <string>("id"), out id))
                {
                    context.Errors.Add(new ExecutionError("Wrong value for guid"));
                    return(null);
                }
                return(routeNetwork.GetRouteSegmentInfo(id));
            }
                );

            Field <ConduitServiceQuery>("conduitService", resolve: context => new { });
            Field <DiagramServiceQuery>("diagramService", resolve: context => new { });
        }
Esempio n. 2
0
        public void Split(Guid nodeId, IRouteNetworkState routeQueryService)
        {
            var segmentInfo = routeQueryService.GetRouteSegmentInfo(this.Id);

            var nodeInfo = routeQueryService.GetRouteNodeInfo(nodeId);

            // Check that split node is close enough to the segment to be splitted
            var line = ConvertFromLineGeoJson(segmentInfo.Geometry.GeoJsonCoordinates);

            var nodePoint = ConvertFromPointGeoJson(nodeInfo.Geometry.GeoJsonCoordinates);

            var test = line.Distance(nodePoint);

            if (test > 0.000001)
            {
                throw new ArgumentException("Coordinate of node used for splitting the segment is not within allowed distance to segment. The distance is greather than 0.000001 decimal degress (around 5-10 cm) which is not allowed.");
            }

            PlannedRouteSegmentSplitByNode segmentSplitEvent = new PlannedRouteSegmentSplitByNode()
            {
                Id = this.Id, SplitNodeId = nodeId
            };

            RaiseEvent(segmentSplitEvent);
        }
Esempio n. 3
0
        private void CreateSegment(string fromName, string toName)
        {
            RouteNodeInfo fromNode = _nodesByName[fromName];
            RouteNodeInfo toNode   = _nodesByName[toName];

            var fixture = new Fixture();

            // Add segment between the two nodes
            var addSegmentCmd = fixture.Build <AddSegmentCommand>()
                                .With(x => x.FromNodeId, fromNode.Id)
                                .With(x => x.ToNodeId, toNode.Id)
                                .With(x => x.Geometry, new Geometry("LineString", "[" + fromNode.Geometry.GeoJsonCoordinates + "," + toNode.Geometry.GeoJsonCoordinates + "]"))
                                .With(x => x.SegmentKind, RouteSegmentKindEnum.Underground)
                                .Create();

            _bus.Send(addSegmentCmd);

            _segmentsByName[fromName + "_" + toName] = _queryService.GetRouteSegmentInfo(addSegmentCmd.Id);
        }
Esempio n. 4
0
        public void SplitRouteSegment(Guid routeSegmentId, Guid splittingNodeId)
        {
            // Id check
            if (routeSegmentId == null || routeSegmentId == Guid.Empty)
            {
                throw new ArgumentException("Id cannot be null or empty");
            }

            // Check that segment exists
            if (!routeNetworkState.CheckIfRouteSegmentIdExists(routeSegmentId))
            {
                throw new ArgumentException("Cannot find any segment in the network with id:" + routeSegmentId);
            }

            // Check that node exists
            if (!routeNetworkState.CheckIfRouteNodeIdExists(splittingNodeId))
            {
                throw new ArgumentException("Cannot find any node in the network with id: " + splittingNodeId);
            }

            var segmentInfo = routeNetworkState.GetRouteSegmentInfo(routeSegmentId);

            var nodeInfo = routeNetworkState.GetRouteNodeInfo(splittingNodeId);

            // Check that split node is close enough to the segment to be splitted
            var line = GeometryConversionHelper.ConvertFromLineGeoJson(segmentInfo.Geometry.GeoJsonCoordinates);

            var nodePoint = GeometryConversionHelper.ConvertFromPointGeoJson(nodeInfo.Geometry.GeoJsonCoordinates);

            var test = line.Distance(nodePoint);

            if (test > 0.000001)
            {
                throw new ArgumentException("Coordinate of node used for splitting the segment is not within allowed distance to segment. The distance is greather than 0.000001 decimal degress (around 5-10 cm) which is not allowed.");
            }

            PlannedRouteSegmentSplitByNode segmentSplitEvent = new PlannedRouteSegmentSplitByNode()
            {
                Id = routeSegmentId, SplitNodeId = splittingNodeId
            };

            RaiseEvent(segmentSplitEvent, false);
        }
Esempio n. 5
0
        public ConduitSegment(IRouteNetworkState routeNetworkQueryService, IConduitNetworkQueryService conduitNetworkEqueryService, IDataLoaderContextAccessor dataLoader, IHttpContextAccessor httpContextAccessor)
        {
            this.routeNetworkQueryService    = routeNetworkQueryService;
            this.conduitNetworkEqueryService = conduitNetworkEqueryService;

            var traversal = new TraversalHelper(routeNetworkQueryService);

            Description = "A conduit will initially contain one segment that spans the whole length of conduit that was originally placed in the route network. When the user starts to cut the conduit at various nodes, more conduit segments will emerge. However, the original conduit asset is the same, now just cut in pieces. The segment represent the pieces. The line represent the original asset. Graph connectivity is maintained on segment level. Use line field to access general asset information. Use the conduit field to access conduit specific asset information.";

            // Interface fields

            Interface <SegmentInterface>();

            Field(x => x.Id, type: typeof(IdGraphType)).Description("Guid property");

            Field <LineSegmentRelationTypeEnumType>(
                "RelationType",
                resolve: context =>
            {
                Guid routeNodeId = (Guid)httpContextAccessor.HttpContext.Items["routeNodeId"];
                return(context.Source.RelationType(routeNodeId));
            });

            Field(x => x.Line.LineKind, type: typeof(LineSegmentKindType)).Description("Type of line segment - i.e. multi conduit, single conduit, fiber cable etc.");

            Field(x => x.Line, type: typeof(LineInterface)).Description("Line that this segment belongs to.");

            Field(x => x.Parents, type: typeof(ListGraphType <SegmentInterface>)).Description("The parent segments of this segment, if this segment is contained within another segment network - i.e. a fiber cable segment running within one of more conduit segments.");

            Field(x => x.Children, type: typeof(ListGraphType <SegmentInterface>)).Description("The child segments of this segment. As an example, if this is multi conduit, then child segments might be fiber cable segments or inner conduit segments running inside the multi conduit.");

            Field <RouteNodeType>(
                "FromRouteNode",
                resolve: context =>
            {
                return(routeNetworkQueryService.GetRouteNodeInfo(context.Source.FromRouteNodeId));
            });

            Field <RouteNodeType>(
                "ToRouteNode",
                resolve: context =>
            {
                return(routeNetworkQueryService.GetRouteNodeInfo(context.Source.ToRouteNodeId));
            });

            Field <SegmentTraversalType>(
                "Traversal",
                resolve: context =>
            {
                return(traversal.CreateTraversalInfoFromSegment(context.Source));
            });

            // Additional fields

            Field(x => x.Conduit, type: typeof(ConduitInfoType)).Description("The original conduit that this segment belongs to.");

            Field <SegmentTraversalType>(
                "Connectivity",
                resolve: context =>
            {
                return(traversal.CreateTraversalInfoFromSegment(context.Source));
            });


            Field <ListGraphType <RouteSegmentType> >(
                "AllRouteSegments",
                resolve: context =>
            {
                List <RouteSegmentInfo> result = new List <RouteSegmentInfo>();

                var woi = routeNetworkQueryService.GetWalkOfInterestInfo(context.Source.Conduit.GetRootConduit().WalkOfInterestId).SubWalk2(context.Source.FromRouteNodeId, context.Source.ToRouteNodeId);

                foreach (var segmentId in woi.AllSegmentIds)
                {
                    result.Add(routeNetworkQueryService.GetRouteSegmentInfo(segmentId));
                }

                return(result);
            });

            Field <ListGraphType <RouteNodeType> >(
                "AllRouteNodes",
                resolve: context =>
            {
                List <RouteNodeInfo> result = new List <RouteNodeInfo>();

                var woi = routeNetworkQueryService.GetWalkOfInterestInfo(context.Source.Conduit.GetRootConduit().WalkOfInterestId).SubWalk2(context.Source.FromRouteNodeId, context.Source.ToRouteNodeId);

                foreach (var nodeId in woi.AllNodeIds)
                {
                    result.Add(routeNetworkQueryService.GetRouteNodeInfo(nodeId));
                }

                return(result);
            });
        }
Esempio n. 6
0
        public Fiber(IRouteNetworkState routeNetworkQueryService, IConduitNetworkQueryService conduitNetworkEqueryService, IDataLoaderContextAccessor dataLoader)
        {
            this.routeNetworkQueryService    = routeNetworkQueryService;
            this.conduitNetworkEqueryService = conduitNetworkEqueryService;

            Description = "A fiber cable.";

            // Interface fields

            Interface <LineInterface>();

            Field(x => x.Id, type: typeof(IdGraphType)).Description("Guid property");

            Field(x => x.LineKind, type: typeof(LineSegmentKindType)).Description("Type of line - i.e. multi conduit, single conduit, fiber cable etc.");

            Field(x => x.FromRouteNode, type: typeof(NodeInterface)).Description("The node where this line equipment starts.");

            Field(x => x.ToRouteNode, type: typeof(NodeInterface)).Description("The node where this line equipment ends.");

            Field(x => x.Parent, type: typeof(LineInterface)).Description("The parent, if this object is part of a composite equipment structure - i.e. a fiber inside a fiber cable or an inner conduit inside a multi conduit. Notice that the parent-child relationship on line level only cover the relationship inside a single composite equipment such as a fiber cable or multi conduit. Containment relationships between different types of equipment is on segment level only.");

            //Field(x => x.Children, type: typeof(ListGraphType<LineInterface>)).Description("The children of a composite equipment structure - i.e. inner conduits part of a multi conduit. Notice that the parent-child relationship on line level only cover the relationship inside a composite equipment such as a fiber cable or multi conduit. Containment relationships between different types of equipment is on segment level only.");

            Field <ListGraphType <LineInterface> > (
                "Children",
                resolve: context =>
            {
                return(null);
            });

            // Fiber specific fields
            Field(x => x.SequenceNumber, type: typeof(IdGraphType)).Description("The position of fiber cable inside of conduit or route.");

            Field <ListGraphType <RouteSegmentType> >(
                "AllRouteSegments",
                resolve: context =>
            {
                List <RouteSegmentInfo> result = new List <RouteSegmentInfo>();

                var woi = routeNetworkQueryService.GetWalkOfInterestInfo(context.Source.GetRoot().WalkOfInterestId);

                foreach (var segmentId in woi.AllSegmentIds)
                {
                    result.Add(routeNetworkQueryService.GetRouteSegmentInfo(segmentId));
                }

                return(result);
            });

            Field <ListGraphType <RouteNodeType> >(
                "AllRouteNodes",
                resolve: context =>
            {
                List <RouteNodeInfo> result = new List <RouteNodeInfo>();

                var woi = routeNetworkQueryService.GetWalkOfInterestInfo(context.Source.GetRoot().WalkOfInterestId);

                foreach (var nodeId in woi.AllNodeIds)
                {
                    result.Add(routeNetworkQueryService.GetRouteNodeInfo(nodeId));
                }

                return(result);
            });
        }
        public ConduitInfoType(IRouteNetworkState routeNetworkQueryService, IConduitNetworkQueryService conduitNetworkEqueryService, IDataLoaderContextAccessor dataLoader)
        {
            this.routeNetworkQueryService    = routeNetworkQueryService;
            this.conduitNetworkEqueryService = conduitNetworkEqueryService;

            Description = "A conduit. Can be a multi conduit (i.e. has inner ducts) or a single conduit.";

            // Interface fields

            //Interface<LineInterface>();

            Field(x => x.Id, type: typeof(IdGraphType)).Description("Guid property");

            Field(x => x.LineKind, type: typeof(LineSegmentKindType)).Description("Type of line - i.e. multi conduit, single conduit, fiber cable etc.");

            Field(x => x.FromRouteNode, type: typeof(NodeInterface)).Description("The node where this line equipment starts.");

            Field(x => x.ToRouteNode, type: typeof(NodeInterface)).Description("The node where this line equipment ends.");

            //Field(x => x.Parent, type: typeof(LineInterface)).Description("The parent, if this object is part of a composite equipment structure - i.e. a fiber inside a fiber cable or an inner conduit inside a multi conduit. Notice that the parent-child relationship on line level only cover the relationship inside a single composite equipment such as a fiber cable or multi conduit. Containment relationships between different types of equipment is on segment level only.");


            Field <ConduitInfoType>(
                "Parent",
                "The parent, if this object is part of a composite equipment structure - i.e. a fiber inside a fiber cable or an inner conduit inside a multi conduit. Notice that the parent-child relationship on line level only cover the relationship inside a single composite equipment such as a fiber cable or multi conduit. Containment relationships between different types of equipment is on segment level only.",
                resolve: context =>
            {
                return(context.Source.Parent);
            });


            // Additional fields, some for backwards compabtiblely

            Field <ConduitKindEnumType>("Kind", "Kind of conduit (multi or single conduit)");
            Field(x => x.Name, type: typeof(IdGraphType)).Description("The uility might give each conduit a name/number");

            Field <IdGraphType>(
                "Position",
                "The position of the conduit inside a multi conduit. Field only populated on inner conduits (conduits inside a multi conduit)",
                resolve: context =>
            {
                return(context.Source.SequenceNumber);
            });

            //Field(x => x.SequenceNumber, type: typeof(IdGraphType)).Description("The position of the conduit inside a multi conduit. Field only populated on inner conduits (conduits inside a multi conduit)");


            Field <ConduitShapeKindEnumType>("Shape", "Shape of conduit - flat, round etc.");
            Field <ConduitColorEnumType>("Color", "Color of the conduit itself");
            Field <ConduitColorEnumType>("ColorMarking", "Normally a colored stripe to distinguish between many conduits of same type in a trench");
            Field(x => x.TextMarking, type: typeof(IdGraphType)).Description("Normally some text printed along the conduitto distinguish between many conduits of same type in a trench");
            Field(x => x.InnerDiameter, type: typeof(IdGraphType)).Description("Inner diameter of the conduit");
            Field(x => x.OuterDiameter, type: typeof(IdGraphType)).Description("Outer diameter of the conduit");
            Field(x => x.AssetInfo, type: typeof(AssetInfoType)).Description("Asset info");

            Field(x => x.Children, type: typeof(ListGraphType <ConduitInfoType>)).Description("Child conduits. Field only populated on multi conduits.");
            //Field(x => x.Parent, type: typeof(ConduitInfoType)).Description("The parent of an inner conduit. Not available on multi and single conduits.");

            /*
             * Field<RouteNodeType>(
             * "FromRouteNode",
             * resolve: context =>
             * {
             *  var woi = routeNetworkQueryService.GetWalkOfInterestInfo(context.Source.GetRootConduit().WalkOfInterestId);
             *  return routeNetworkQueryService.GetRouteNodeInfo(woi.StartNodeId);
             * });
             *
             * Field<RouteNodeType>(
             * "ToRouteNode",
             * resolve: context =>
             * {
             *  var woi = routeNetworkQueryService.GetWalkOfInterestInfo(context.Source.GetRootConduit().WalkOfInterestId);
             *  return routeNetworkQueryService.GetRouteNodeInfo(woi.EndNodeId);
             * });
             */

            Field <ListGraphType <RouteSegmentType> >(
                "AllRouteSegments",
                resolve: context =>
            {
                List <RouteSegmentInfo> result = new List <RouteSegmentInfo>();

                var woi = routeNetworkQueryService.GetWalkOfInterestInfo(context.Source.GetRootConduit().WalkOfInterestId);

                foreach (var segmentId in woi.AllSegmentIds)
                {
                    result.Add(routeNetworkQueryService.GetRouteSegmentInfo(segmentId));
                }

                return(result);
            });

            Field <ListGraphType <RouteNodeType> >(
                "AllRouteNodes",
                resolve: context =>
            {
                List <RouteNodeInfo> result = new List <RouteNodeInfo>();

                var woi = routeNetworkQueryService.GetWalkOfInterestInfo(context.Source.GetRootConduit().WalkOfInterestId);

                foreach (var nodeId in woi.AllNodeIds)
                {
                    result.Add(routeNetworkQueryService.GetRouteNodeInfo(nodeId));
                }

                return(result);
            });
        }
        public ConduitSegmentType(IRouteNetworkState routeNetworkQueryService, IConduitNetworkQueryService conduitNetworkEqueryService, IDataLoaderContextAccessor dataLoader)
        {
            this.routeNetworkQueryService    = routeNetworkQueryService;
            this.conduitNetworkEqueryService = conduitNetworkEqueryService;

            var traversal = new TraversalHelper(routeNetworkQueryService);

            Description = "A conduit segment will initially be the original whole length piece of conduit. When the user starts to cut the conduit at various nodes, more conduit segments will emerge. Graph connectivity is maintained on segment level.";

            Field(x => x.Id, type: typeof(IdGraphType)).Description("Guid property");

            Field(x => x.Conduit, type: typeof(ConduitInfoType)).Description("The original conduit that this segment is part of.");

            Field(x => x.Children, type: typeof(ListGraphType <ConduitSegmentType>)).Description("The children of a multi conduit segment.");
            Field(x => x.Parents, type: typeof(ListGraphType <ConduitSegmentType>)).Description("The parents of an inner conduit segment.");

            Field <ConduitLineType>(
                "Line",
                resolve: context =>
            {
                var traversalInfo = traversal.CreateTraversalInfoFromSegment(context.Source);

                var cLineType               = new ConduitLineInfo();
                cLineType.AllSegments       = traversalInfo.AllSegments.OfType <ConduitSegmentInfo>().ToList();
                cLineType.AllRouteNodes     = traversalInfo.AllRouteNodes.OfType <RouteNodeInfo>().ToList();
                cLineType.AllRouteSegments  = traversalInfo.AllRouteSegments.OfType <RouteSegmentInfo>().ToList();
                cLineType.EndRouteNode      = (RouteNodeInfo)traversalInfo.EndRouteNode;
                cLineType.StartRouteNode    = (RouteNodeInfo)traversalInfo.StartRouteNode;
                cLineType.StartRouteSegment = (RouteSegmentInfo)traversalInfo.StartRouteSegment;
                cLineType.EndRouteSegment   = (RouteSegmentInfo)traversalInfo.EndRouteSegment;

                return(cLineType);
            });

            Field <RouteNodeType>(
                "FromRouteNode",
                resolve: context =>
            {
                return(routeNetworkQueryService.GetRouteNodeInfo(context.Source.FromRouteNodeId));
            });

            Field <RouteNodeType>(
                "ToRouteNode",
                resolve: context =>
            {
                return(routeNetworkQueryService.GetRouteNodeInfo(context.Source.ToRouteNodeId));
            });

            Field <ListGraphType <RouteSegmentType> >(
                "AllRouteSegments",
                resolve: context =>
            {
                List <RouteSegmentInfo> result = new List <RouteSegmentInfo>();

                var woi = routeNetworkQueryService.GetWalkOfInterestInfo(context.Source.Conduit.GetRootConduit().WalkOfInterestId).SubWalk2(context.Source.FromRouteNodeId, context.Source.ToRouteNodeId);

                foreach (var segmentId in woi.AllSegmentIds)
                {
                    result.Add(routeNetworkQueryService.GetRouteSegmentInfo(segmentId));
                }

                return(result);
            });

            Field <ListGraphType <RouteNodeType> >(
                "AllRouteNodes",
                resolve: context =>
            {
                List <RouteNodeInfo> result = new List <RouteNodeInfo>();

                var woi = routeNetworkQueryService.GetWalkOfInterestInfo(context.Source.Conduit.GetRootConduit().WalkOfInterestId).SubWalk2(context.Source.FromRouteNodeId, context.Source.ToRouteNodeId);

                foreach (var nodeId in woi.AllNodeIds)
                {
                    result.Add(routeNetworkQueryService.GetRouteNodeInfo(nodeId));
                }

                return(result);
            });

            Field(x => x.Line.LineKind, type: typeof(LineSegmentKindType)).Description("Type of line segment - i.e. conduit, power cable, signal cable etc.");


            //Interface<LineSegmentInterface>();
        }
Esempio n. 9
0
        public ISegmentTraversal CreateTraversalInfoFromSegment(ISegment sourceSegment)
        {
            var result = new SegmentTraversalInfo();

            HashSet <Guid> startNodesFound = new HashSet <Guid>();
            HashSet <Guid> endNodesFound   = new HashSet <Guid>();

            Guid startNodeId    = Guid.Empty;
            Guid endNodeId      = Guid.Empty;
            Guid startSegmentId = Guid.Empty;
            Guid endSegmentId   = Guid.Empty;

            List <Guid> allNodeIds    = new List <Guid>();
            List <Guid> allSegmentIds = new List <Guid>();

            HashSet <ILine> alreadyChecked = new HashSet <ILine>();

            // Get all segments related to the source segment
            var traceResult = sourceSegment.UndirectionalDFS <GraphElement, GraphElement>();

            // Pull out the segments from the trace result
            var segments = traceResult.Where(t => t is ISegment).Select(t => t as ISegment);

            foreach (var segment in segments)
            {
                var rootConduit = segment.Line.GetRoot();

                if (!alreadyChecked.Contains(rootConduit))
                {
                    alreadyChecked.Add(rootConduit);

                    var walkOfInterest = routeNetworkQueryService.GetWalkOfInterestInfo(rootConduit.WalkOfInterestId).SubWalk2(segment.FromRouteNodeId, segment.ToRouteNodeId);

                    // add node ids
                    foreach (var nodeId in walkOfInterest.AllNodeIds)
                    {
                        if (!allNodeIds.Contains(nodeId))
                        {
                            allNodeIds.Add(nodeId);
                        }
                    }

                    // add segment ids
                    foreach (var segmentId in walkOfInterest.AllSegmentIds)
                    {
                        if (!allSegmentIds.Contains(segmentId))
                        {
                            allSegmentIds.Add(segmentId);
                        }
                    }


                    if (!startNodesFound.Contains(walkOfInterest.StartNodeId))
                    {
                        startNodesFound.Add(walkOfInterest.StartNodeId);
                        startNodesFound.Add(walkOfInterest.EndNodeId);
                        startNodeId    = walkOfInterest.StartNodeId;
                        startSegmentId = walkOfInterest.StartSegmentId;
                    }

                    if (!endNodesFound.Contains(walkOfInterest.EndNodeId))
                    {
                        endNodesFound.Add(walkOfInterest.StartNodeId);
                        endNodesFound.Add(walkOfInterest.EndNodeId);
                        endNodeId    = walkOfInterest.EndNodeId;
                        endSegmentId = walkOfInterest.EndSegmentId;
                    }
                }
            }

            result.StartRouteNode    = routeNetworkQueryService.GetRouteNodeInfo(startNodeId);
            result.EndRouteNode      = routeNetworkQueryService.GetRouteNodeInfo(endNodeId);
            result.StartRouteSegment = routeNetworkQueryService.GetRouteSegmentInfo(startSegmentId);
            result.EndRouteSegment   = routeNetworkQueryService.GetRouteSegmentInfo(endSegmentId);

            result.AllRouteNodes = new List <INode>();
            foreach (var nodeId in allNodeIds)
            {
                result.AllRouteNodes.Add(routeNetworkQueryService.GetRouteNodeInfo(nodeId));
            }

            result.AllRouteSegments = new List <ISegment>();
            foreach (var segmentId in allSegmentIds)
            {
                result.AllRouteSegments.Add(routeNetworkQueryService.GetRouteSegmentInfo(segmentId));
            }

            result.AllSegments = segments.ToList();

            return(result);
        }
        public Task <Unit> Handle(RegisterWalkOfInterestCommand request, CancellationToken cancellationToken)
        {
            // Id check
            if (request.WalkOfInterestId == null || request.WalkOfInterestId == Guid.Empty)
            {
                throw new ArgumentException("Id cannot be null or empty");
            }

            // Basic check that some route element ids are filled in at all
            if (request.RouteElementIds == null || request.RouteElementIds.Count < 3)
            {
                throw new ArgumentException("The route element id list is empty or has less than 3 ids, which cannot possible be a valid walk. A minumum walk is from one node, through a segment, to another node (node->segment->node) - i.e. 3 ids.");
            }

            // Check if the chain of route element ids are valid (is proper connected to each other in the route network graph)
            bool         shouldBeNode      = true;
            bool         firstElement      = true;
            GraphElement previousElement   = null;
            Guid         previousElementId = Guid.Empty;

            int position = 1;

            foreach (var routeElementId in request.RouteElementIds)
            {
                GraphElement currentElement = null;

                // Node
                if (shouldBeNode)
                {
                    if (!routeQueryService.CheckIfRouteNodeIdExists(routeElementId))
                    {
                        throw new ArgumentException("Route element id: " + routeElementId + " at position: " + position + " (in the route element ids property) is expected to be a node. But no node could be found with that id.");
                    }

                    currentElement = routeQueryService.GetRouteNodeInfo(routeElementId);

                    shouldBeNode = false;
                }

                // Segment
                else if (!shouldBeNode)
                {
                    if (!routeQueryService.CheckIfRouteSegmentIdExists(routeElementId))
                    {
                        throw new ArgumentException("Route element id: " + routeElementId + " at position: " + position + " (in the route element ids property) is expected to be a segment. But no segment could be found with that id.");
                    }

                    currentElement = routeQueryService.GetRouteSegmentInfo(routeElementId);

                    shouldBeNode = true;
                }

                // Check that the element is connected to the previous specified element in the route network graph
                if (!firstElement)
                {
                    if (!currentElement.NeighborElements.Exists(n => n == previousElement))
                    {
                        throw new ArgumentException("Route element id: " + routeElementId + " at position: " + position + " (in the route element ids property) is not neighboor to previous id: " + previousElementId + " according to the route network graph. Please check that the walk is valid.");
                    }
                }

                position++;
                firstElement      = false;
                previousElementId = routeElementId;
                previousElement   = currentElement;
            }

            // Check if first id is a node
            if (!(routeQueryService.GetRouteElementInfo(request.RouteElementIds[0]) is RouteNodeInfo))
            {
                throw new ArgumentException("First route element id (in the route element ids property) must be a node, but it's not.");
            }

            // Check if last id is a node
            if (!(routeQueryService.GetRouteElementInfo(request.RouteElementIds[request.RouteElementIds.Count - 1]) is RouteNodeInfo))
            {
                throw new ArgumentException("Last route element id (in the route element ids property) must be a node, but it's not.");
            }


            var walkOfInterest = new WalkOfInterest(request.WalkOfInterestId, request.RouteElementIds);

            repo.Store(walkOfInterest);

            return(Unit.Task);
        }