/// <summary>
        /// Create sample polyline feature using the geometries from the point feature layer.
        /// </summary>
        /// <param name="polylineLayer">Polyline geometry feature layer used to add the new features.</param>
        /// <param name="pointLayer">The geometries from the point layer are used as vertices for the new line features.</param>
        /// <returns></returns>
        private Task <bool> constructSamplePolylines(FeatureLayer polylineLayer, FeatureLayer pointLayer)
        {
            // execute the fine grained API calls on the CIM main thread
            return(QueuedTask.Run(() =>
            {
                // get the underlying feature class for each layer
                var polylineFeatureClass = polylineLayer.GetTable() as FeatureClass;
                var pointFeatureClass = pointLayer.GetTable() as FeatureClass;

                // retrieve the feature class schema information for the feature classes
                var polylineDefinition = polylineFeatureClass.GetDefinition() as FeatureClassDefinition;
                var pointDefinition = pointFeatureClass.GetDefinition() as FeatureClassDefinition;

                // construct a cursor for all point features, since we want all feature there is no
                // QueryFilter required
                var pointCursor = pointFeatureClass.Search(null, false);

                // initialize a counter variable
                int pointCounter = 0;
                // initialize a list to hold 5 coordinates that are used as vertices for the polyline
                var lineCoordinates = new List <Coordinate>(5);

                // set up the edit operation for the feature creation
                var createOperation = new EditOperation();
                createOperation.Name = "Create polylines";
                createOperation.SelectNewFeatures = false;

                // set up the datum transformation to be used in the projection
                ProjectionTransformation transformation = ProjectionTransformation.CreateFromEnvironment(pointDefinition.GetSpatialReference(),
                                                                                                         polylineDefinition.GetSpatialReference());

                // loop through the point features
                while (pointCursor.MoveNext())
                {
                    pointCounter++;

                    var pointFeature = pointCursor.Current as Feature;
                    // add the feature point geometry as a coordinate into the vertex list of the line
                    // - ensure that the projection of the point geometry is converted to match the spatial reference of the line
                    // with a datum transformation considering the different spheroids
                    lineCoordinates.Add(((MapPoint)GeometryEngine.ProjectEx(pointFeature.GetShape(), transformation)).Coordinate);

                    // for every 5 geometries, construct a new polyline and queue a feature create
                    if (pointCounter % 5 == 0)
                    {
                        // construct a new polyline by using the 5 point coordinate in the current list
                        var newPolyline = PolylineBuilder.CreatePolyline(lineCoordinates, polylineDefinition.GetSpatialReference());
                        // queue the create operation as part of the edit operation
                        createOperation.Create(polylineLayer, newPolyline);
                        // reset the list of coordinates
                        lineCoordinates = new List <Coordinate>(5);
                    }
                }

                // execute the edit (create) operation
                return createOperation.ExecuteAsync();
            }));
        }
Beispiel #2
0
        /// <summary>
        /// Creates a new camera offset from the provided camera around an ellipse.
        /// </summary>
        /// <param name="camera">The starting camera.</param>
        /// <param name="ellipse">The ellipse around which the camera will rotate.</param>
        /// <param name="centerPoint">The center point of the ellipse.</param>
        /// <param name="percentAlong">The percentage around the ellipse to create the camera.</param>
        private Camera OffsetCamera(Camera camera, Polyline ellipse, MapPoint centerPoint, double percentAlong)
        {
            camera = CloneCamera(camera);

            var fromPoint = GeometryEngine.MovePointAlongLine(ellipse, percentAlong, true, 0);

            var segment = LineBuilder.CreateLineSegment(new Coordinate2D(centerPoint.X, centerPoint.Y), new Coordinate2D(fromPoint.X, centerPoint.Y), centerPoint.SpatialReference);
            var difX    = GeometryEngine.GeodesicLength(PolylineBuilder.CreatePolyline(segment, segment.SpatialReference));

            if (centerPoint.X - fromPoint.X < 0)
            {
                difX *= -1;
            }

            segment = LineBuilder.CreateLineSegment(new Coordinate2D(centerPoint.X, centerPoint.Y), new Coordinate2D(centerPoint.X, fromPoint.Y), centerPoint.SpatialReference);
            var difY = GeometryEngine.GeodesicLength(PolylineBuilder.CreatePolyline(segment, segment.SpatialReference));

            if (centerPoint.Y - fromPoint.Y < 0)
            {
                difY *= -1;
            }

            var radian  = Math.Atan2(difX, difY);
            var heading = radian * -180 / Math.PI;

            camera.Heading = heading;

            var difZ       = centerPoint.Z - (camera.Z * ((camera.SpatialReference.IsGeographic) ? 1.0 : camera.SpatialReference.Unit.ConversionFactor));
            var hypotenuse = GeometryEngine.GeodesicDistance(fromPoint, centerPoint);

            radian = Math.Atan2(difZ, hypotenuse);
            var pitch = radian * 180 / Math.PI;

            camera.Pitch = pitch;

            if (fromPoint.SpatialReference.Wkid != camera.SpatialReference.Wkid)
            {
                var transformation = ProjectionTransformation.Create(fromPoint.SpatialReference, camera.SpatialReference);
                fromPoint = GeometryEngine.ProjectEx(fromPoint, transformation) as MapPoint;
            }

            camera.X = fromPoint.X;
            camera.Y = fromPoint.Y;
            return(camera);
        }
Beispiel #3
0
        /// <summary>
        /// Creates keyframes along the path using the user defined settings.
        /// </summary>
        /// <param name="line">The geometry of the line to fly along.</param>
        /// <param name="verticalUnit">The elevation unit of the 3D layer</param>
        internal Task CreateKeyframesAlongPath(Polyline line, Unit verticalUnit)
        {
            return(QueuedTask.Run(() =>
            {
                var mapView = MapView.Active;
                if (mapView == null)
                {
                    return;
                }

                //Get the camera track from the active map's animation.
                //There will always be only one camera track in the animation.
                var cameraTrack = mapView.Map.Animation.Tracks.OfType <CameraTrack>().First();

                //Get some of the user settings for constructing the keyframes alone the path.
                var densifyDistance = Animation.Settings.KeyEvery;
                var verticalOffset = Animation.Settings.HeightAbove / ((mapView.Map.SpatialReference.IsGeographic) ? 1.0 : mapView.Map.SpatialReference.Unit.ConversionFactor); //1 meter
                double currentTimeSeconds = GetInsertTime(mapView.Map.Animation);

                //We need to project the line to a projected coordinate system to calculate the line's length in 3D
                //as well as more accurately calculated heading and pitch along the path.
                if (line.SpatialReference.IsGeographic)
                {
                    if (mapView.Map.SpatialReference.IsGeographic)
                    {
                        var transformation = ProjectionTransformation.Create(line.SpatialReference, SpatialReferences.WebMercator, line.Extent);
                        line = GeometryEngine.ProjectEx(line, transformation) as Polyline;
                    }
                    else
                    {
                        var transformation = ProjectionTransformation.Create(line.SpatialReference, mapView.Map.SpatialReference, line.Extent);
                        line = GeometryEngine.ProjectEx(line, transformation) as Polyline;
                    }
                }

                //If the user has specified to create keyframes at additional locations than just the vertices
                //we will densify the line by the distance the user specified.
                if (!Animation.Settings.VerticesOnly)
                {
                    line = GeometryEngine.DensifyByLength(line, densifyDistance / line.SpatialReference.Unit.ConversionFactor) as Polyline;
                }

                //To maintain a constant speed we need to divide the total time we want the animation to take by the length of the line.
                var duration = Animation.Settings.Duration;
                var secondsPerUnit = duration / line.Length3D;
                Camera prevCamera = null;

                //Loop over each vertex in the line and create a new keyframe at each.
                for (int i = 0; i < line.PointCount; i++)
                {
                    #region Camera

                    MapPoint cameraPoint = line.Points[i];

                    //If the point is not in the same spatial reference of the map we need to project it.
                    if (cameraPoint.SpatialReference.Wkid != mapView.Map.SpatialReference.Wkid)
                    {
                        var transformation = ProjectionTransformation.Create(cameraPoint.SpatialReference, mapView.Map.SpatialReference);
                        cameraPoint = GeometryEngine.Project(cameraPoint, mapView.Map.SpatialReference) as MapPoint;
                    }

                    //Construct a new camera from the point.
                    var camera = new Camera(cameraPoint.X, cameraPoint.Y, cameraPoint.Z,
                                            Animation.Settings.Pitch, 0.0, cameraPoint.SpatialReference, CameraViewpoint.LookFrom);

                    //Convert the Z unit to meters if the camera is not in a geographic coordinate system.
                    if (!camera.SpatialReference.IsGeographic)
                    {
                        camera.Z /= camera.SpatialReference.Unit.ConversionFactor;
                    }

                    //Convert the Z to the unit of the layer's elevation unit and then add the user defined offset from the line.
                    camera.Z *= verticalUnit.ConversionFactor;
                    camera.Z += verticalOffset;

                    //If this is the last point in the collection use the same heading and pitch from the previous camera.
                    if (i + 1 == line.Points.Count)
                    {
                        camera.Heading = prevCamera.Heading;
                        camera.Pitch = prevCamera.Pitch;
                    }
                    else
                    {
                        var currentPoint = line.Points[i];
                        var nextPoint = line.Points[i + 1];

                        #region Heading

                        //Calculate the heading from the current point to the next point in the path.
                        var difX = nextPoint.X - currentPoint.X;
                        var difY = nextPoint.Y - currentPoint.Y;
                        var radian = Math.Atan2(difX, difY);
                        var heading = radian * -180 / Math.PI;
                        camera.Heading = heading;

                        #endregion

                        #region Pitch

                        //If the user doesn't want to hardcode the pitch, calculate the pitch based on the current point to the next point.
                        if (Animation.Settings.UseLinePitch)
                        {
                            var hypotenuse = Math.Sqrt(Math.Pow(difX, 2) + Math.Pow(difY, 2));
                            var difZ = nextPoint.Z - currentPoint.Z;
                            //If the line's unit is not the same as the elevation unit of the layer we need to convert the Z so they are in the same unit.
                            if (line.SpatialReference.Unit.ConversionFactor != verticalUnit.ConversionFactor)
                            {
                                difZ *= (verticalUnit.ConversionFactor / line.SpatialReference.Unit.ConversionFactor);
                            }
                            radian = Math.Atan2(difZ, hypotenuse);
                            var pitch = radian * 180 / Math.PI;
                            camera.Pitch = pitch;
                        }
                        else
                        {
                            camera.Pitch = Animation.Settings.Pitch;
                        }

                        #endregion
                    }

                    #endregion

                    #region Time

                    //The first point will have a time of 0 seconds, after that we need to set the time based on the 3D distance between the points.
                    if (i > 0)
                    {
                        var lineSegment = PolylineBuilder.CreatePolyline(new List <MapPoint>()
                        {
                            line.Points[i - 1], line.Points[i]
                        },
                                                                         line.SpatialReference);
                        var length = lineSegment.Length3D;
                        currentTimeSeconds += length * secondsPerUnit;
                    }

                    #endregion

                    //Create a new keyframe using the camera and the time.
                    cameraTrack.CreateKeyframe(camera, TimeSpan.FromSeconds(currentTimeSeconds), AnimationTransition.Linear);
                    prevCamera = camera;
                }
            }));
        }
Beispiel #4
0
        /// <summary>
        /// Create keyframes centered around a point.
        /// </summary>
        /// <param name="point">The center point around which the keyframes are created.</param>
        internal Task CreateKeyframesAroundPoint(MapPoint point)
        {
            return(QueuedTask.Run(() =>
            {
                var mapView = MapView.Active;
                var degrees = Animation.Settings.Degrees;
                if (mapView == null || degrees == 0)
                {
                    return;
                }

                //Get the camera track from the active map's animation.
                //There will always be only one camera track in the animation.
                var cameraTrack = mapView.Map.Animation.Tracks.OfType <CameraTrack>().First();
                var camera = mapView.Camera;

                //Calculate the number of keys to create.
                var keyEvery = (degrees < 0) ? -10 : 10; //10 degrees
                var numOfKeys = Math.Floor(degrees / keyEvery);
                var remainder = degrees % keyEvery;

                //To maintain a constant speed we need to divide the total time we want the animation to take by the number of degrees of rotation.
                var duration = Animation.Settings.Duration;
                double timeInterval = duration / Math.Abs(degrees);
                double currentTimeSeconds = GetInsertTime(mapView.Map.Animation);

                //Get the distance from the current location to the point we want to rotate around to get the radius.
                var cameraPoint = MapPointBuilder.CreateMapPoint(camera.X, camera.Y, camera.SpatialReference);
                var radius = GeometryEngine.GeodesicDistance(cameraPoint, point);
                var radian = ((camera.Heading - 90) / 180.0) * Math.PI;

                //If the spatial reference of the point is projected and the unit is not in meters we need to convert the Z values to meters.
                if (!point.SpatialReference.IsGeographic && point.SpatialReference.Unit.ConversionFactor != 1.0)
                {
                    point = MapPointBuilder.CreateMapPoint(point.X, point.Y,
                                                           point.Z * point.SpatialReference.Unit.ConversionFactor, point.SpatialReference);
                }

                //For all geodesic calculations we will use WGS84 so we will project the point if it is not already.
                if (point.SpatialReference.Wkid != SpatialReferences.WGS84.Wkid)
                {
                    var transformation = ProjectionTransformation.Create(point.SpatialReference, SpatialReferences.WGS84);
                    point = GeometryEngine.ProjectEx(point, transformation) as MapPoint;
                }

                //Create an ellipse around the center point.
                var parameter = new GeometryEngine.GeodesicEllipseParameter();
                parameter.Center = point.Coordinate;
                parameter.SemiAxis1Length = radius;
                parameter.SemiAxis2Length = radius;
                parameter.AxisDirection = radian;
                parameter.LinearUnit = LinearUnit.Meters;
                parameter.OutGeometryType = GeometryType.Polyline;
                parameter.VertexCount = 36;
                var ellipse = GeometryEngine.GeodesicEllipse(parameter, point.SpatialReference) as Polyline;

                //For each key we will progressively rotate around the ellipse and calculate the camera position at each.
                for (int i = 0; i <= numOfKeys; i++)
                {
                    var percentAlong = ((Math.Abs(keyEvery) * i) % 360) / 360.0;
                    if (keyEvery > 0)
                    {
                        percentAlong = 1 - percentAlong;
                    }

                    //Get the camera at the position around the ellipse.
                    camera = OffsetCamera(camera, ellipse, point, percentAlong);

                    //Increment the time by the amount of time per key.
                    if (i != 0)
                    {
                        currentTimeSeconds += (timeInterval * Math.Abs(keyEvery));
                    }

                    //Create a new keyframe for the camera.
                    cameraTrack.CreateKeyframe(camera, TimeSpan.FromSeconds(currentTimeSeconds), AnimationTransition.FixedArc);
                }

                //For any degree rotation left over create a keyframe. For example 155, would have a keyframe every 10 degrees and then one for the final 5 degrees.
                if (remainder != 0.0)
                {
                    var percentAlong = ((Math.Abs(keyEvery) * numOfKeys + Math.Abs(remainder)) % 360) / 360.0;
                    if (remainder > 0)
                    {
                        percentAlong = 1 - percentAlong;
                    }

                    OffsetCamera(camera, ellipse, point, percentAlong);

                    //Increment the time and create the keyframe.
                    currentTimeSeconds += (timeInterval * Math.Abs(remainder));
                    cameraTrack.CreateKeyframe(camera, TimeSpan.FromSeconds(currentTimeSeconds), AnimationTransition.FixedArc);
                }
            }));
        }