首页 > 其他分享 >ArcGIS Pro 沿线飞行代码

ArcGIS Pro 沿线飞行代码

时间:2022-09-20 14:55:46浏览次数:50  
标签:沿线 double Pro await MapPoint ArcGIS startPt StartPoint endPt

//   Copyright 2019 Esri
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at

//       http://www.apache.org/licenses/LICENSE-2.0

//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License. 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ArcGIS.Desktop.Framework;
using ArcGIS.Desktop.Framework.Contracts;
using ArcGIS.Desktop.Mapping;
using ArcGIS.Core.Data;
using ArcGIS.Core.CIM;
using ArcGIS.Core.Geometry;
using ArcGIS.Desktop.Framework.Threading.Tasks;
using ArcGIS.Desktop.Framework.Dialogs;

namespace AnimationFromPath
{
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
  public static class CreateAnimationFromPath
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
  {
    #region Constants

    //determines where intermediate keyframes are created along a segment
    private const double LINE_CONSTRAINT_FACTOR = 0.15;
    private const double ARC_CONSTRAINT_FACTOR = 0.15;

    //minimum length of a straight line segment needed to create intermediate keyframes
    //and use additional logic for ignoring rotation on end points. If a straight line segment
    //is smaller than this threshold then intermediate keyframes are not created and ignore-rotation-at-end-points
    //logic is not used
    private const double STRAIGHT_SEGMENT_LENGTH_THRESHOLD = 30;

    private const double ANIMATION_APPEND_TIME = 3; //seconds

    #endregion

    #region Fields/Properties

    private static double _keyframeHeading = 0;

    private static double _keyframePitch = 0;

    private static double Z_CONVERSION_FACTOR = 1;

    private static string _selectedMethod = "Keyframes along path";
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
    public static string SelectedMethod
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
    {
      get { return _selectedMethod; }

      set { _selectedMethod = value; }
    }

    private static string _selectedCameraView = "Top down";
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
    public static string SelectedCameraView
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
    {
      get { return _selectedCameraView; }

      set { _selectedCameraView = value; }
    }


    private static double _customPitch = -90;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
    public static double CustomPitch
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
    {
      get { return _customPitch; }

      set { _customPitch = value; }
    }

    private static double _cameraZOffset = 1000;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
    public static double CameraZOffset
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
    {
      get { return _cameraZOffset; }

      set { _cameraZOffset = value; }
    }

    private static double _totalDuration = 0;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
    public static double TotalDuration
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
    {
      get { return _totalDuration; }

      set { _totalDuration = value; }
    }

    private static double _keyEveryNSecond = 1;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
    public static double KeyEveryNSecond
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
    {
      get { return _keyEveryNSecond; }

      set { _keyEveryNSecond = value; }
    }

    private static MapPoint _targetPoint = null;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
    public static MapPoint TargetPoint
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
    {
      get { return _targetPoint; }
      set { _targetPoint = value; }
    }

    #endregion

    #region Functions

#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
    public static async Task CreateKeyframes()
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
    {
      FeatureLayer ftrLayer = null;

      MapView mapView = MapView.Active;
      if (mapView == null)
        return;

      var mapSelection = await QueuedTask.Run(() => MapView.Active.Map.GetSelection());

      if (mapSelection.Count == 1)
      {
        var layer = mapSelection.First().Key;
        if (layer is FeatureLayer)
        {
          ftrLayer = (FeatureLayer)layer;
          if (ftrLayer.ShapeType != ArcGIS.Core.CIM.esriGeometryType.esriGeometryPolyline)
          {
            ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show("Select a polyline feature.");
            return;
          }

          int numFtrsSelected = await QueuedTask.Run(() => ftrLayer.GetSelection().GetCount());

          if (numFtrsSelected != 1)
          {
            ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show("Select only one polyline feature.");
            return;
          }
        }
        else
        {
          ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show("Select a polyline feature.");
          return;
        }
      }
      else
      {
        ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show("Select a polyline feature.");
        return;
      }

      if (SelectedCameraView == "Face target" && TargetPoint == null)
      {
        ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show("Selected view type is - Face target - but a target point is not set.");
        return;
      }

      string oid_fieldName = await QueuedTask.Run(() => ftrLayer.GetTable().GetDefinition().GetObjectIDField());

      //get selected polyline
      Polyline lineGeom = await QueuedTask.Run<Polyline>(() =>
      {
        var selectedFtrOID = MapView.Active.Map.GetSelection()[ftrLayer][0];
        QueryFilter qf = new QueryFilter();
        qf.WhereClause = oid_fieldName + " = " + selectedFtrOID.ToString();
        RowCursor result = ftrLayer.GetFeatureClass().Search(qf);
        if (result != null && result.MoveNext())
        {
              Polyline plyLine = null;
              using (Feature selectedFtr = result.Current as Feature)
              {
                  plyLine = selectedFtr.GetShape().Clone() as Polyline;
              }
              return plyLine;
        }
        return null;
      });

      //couldn't get the selected feature
      if (lineGeom == null)
        return;

      ProjectionTransformation transformation = await QueuedTask.Run(() => ProjectionTransformation.Create(ftrLayer.GetSpatialReference(), mapView.Map.SpatialReference));
      SpatialReference layerSpatRef = await QueuedTask.Run(() => ftrLayer.GetSpatialReference());

      if (layerSpatRef.Unit.Name != "Degree")
        Z_CONVERSION_FACTOR = layerSpatRef.Unit.ConversionFactor;

      //Project target point if method is Face target
      if (SelectedCameraView == "Face target")
      {
        if (TargetPoint != null && TargetPoint.SpatialReference != layerSpatRef)
        {
          ProjectionTransformation transf_forTarget = await QueuedTask.Run(() => ProjectionTransformation.Create(TargetPoint.SpatialReference, layerSpatRef));
          MapPoint projected_targetPoint = (MapPoint)GeometryEngine.Instance.ProjectEx(TargetPoint, transf_forTarget);
          TargetPoint = null;
          TargetPoint = projected_targetPoint;
        }
      }

      var animation = mapView.Map.Animation;
      var cameraTrack = animation.Tracks.OfType<CameraTrack>().First();
      var keyframes = cameraTrack.Keyframes;

      //Get segment list for line
      ReadOnlyPartCollection polylineParts = lineGeom.Parts;

      //get total segment count and determine path length
      double pathLength = 0;
      int segmentCount = 0;
      IEnumerator<ReadOnlySegmentCollection> segments = polylineParts.GetEnumerator();

      while (segments.MoveNext())
      {
        ReadOnlySegmentCollection seg = segments.Current;
        foreach (Segment s in seg)
        {
          //pathLength += s.Length;//s.Length returns 2D length

          double length3D = Math.Sqrt((s.EndPoint.X - s.StartPoint.X) * (s.EndPoint.X - s.StartPoint.X) +
                                        (s.EndPoint.Y - s.StartPoint.Y) * (s.EndPoint.Y - s.StartPoint.Y) +
                                          (s.EndPoint.Z - s.StartPoint.Z) * (s.EndPoint.Z - s.StartPoint.Z));

          pathLength += length3D;
          segmentCount += 1;
        }
      }

      //reset heading and pitch
      _keyframeHeading = 0;
      _keyframePitch = 0;

      // Create keyframes based on chosen method
      if (SelectedMethod == "Keyframes along path")
      {
        await CreateKeyframes_AlongPath(mapView, layerSpatRef, transformation, cameraTrack, segments, segmentCount, pathLength);
      }
      else if (SelectedMethod == "Keyframes every N seconds")
      {
        await CreateKeyframes_EveryNSeconds(mapView, layerSpatRef, transformation, cameraTrack, segments, segmentCount, pathLength, KeyEveryNSecond);
      }
      else if (SelectedMethod == "Keyframes only at vertices")
      {
        await CreateKeyframes_AtVertices(mapView, layerSpatRef, transformation, cameraTrack, lineGeom, segments, segmentCount, pathLength);
      }
    }

    //Use this method for smoother turns at corners. Additionally this method processes straight line segments and arc segments separately
    //For arc segments a keyframe is created at every second. However a minimum of 5 keyframes are created for arcs.
    //So if arc segment length is less than 5 then we default to at least 5 keyframes. This is an attempt to stick to the path as much as possible.
    //For straight line segments, rotation is ignored at end point of each segment except for the end point of the path itself. Two keyframes with rotation
    //are created at certain distance (determined by LINE_CONSTRAINT_FACTOR) before and after the end point of each segment. This is an attempt to avoid
    //sharp turns at corners along the path.
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
    public static async Task CreateKeyframes_AlongPath(MapView mapView, SpatialReference layerSpatRef, ProjectionTransformation transformation,
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
                                                    CameraTrack cameraTrack, IEnumerator<ReadOnlySegmentCollection> segments,
                                                    int segmentCount, double pathLength)
    {
      double segmentLength = 0;
      int num_iterations = 0;
      segments.Reset();

      //process each segment depending upon its type - straight line or arc
      while (segments.MoveNext())
      {
        ReadOnlySegmentCollection seg = segments.Current;
        double accumulatedDuration = mapView.Map.Animation.Duration.TotalSeconds + ((mapView.Map.Animation.Duration.TotalSeconds > 0) ? ANIMATION_APPEND_TIME : 0); // 0;

        foreach (Segment s in seg)
        {
          double length3D = Math.Sqrt((s.EndPoint.X - s.StartPoint.X) * (s.EndPoint.X - s.StartPoint.X) +
                                        (s.EndPoint.Y - s.StartPoint.Y) * (s.EndPoint.Y - s.StartPoint.Y) +
                                          (s.EndPoint.Z - s.StartPoint.Z) * (s.EndPoint.Z - s.StartPoint.Z));

          
          double segmentDuration = (TotalDuration / pathLength) * length3D;
          segmentLength = length3D;

          //straight line segments
          if (s.SegmentType == SegmentType.Line)
          {
            MapPoint startPt = await QueuedTask.Run(() => MapPointBuilder.CreateMapPoint(s.StartPoint.X, s.StartPoint.Y, s.StartPoint.Z * Z_CONVERSION_FACTOR, layerSpatRef));
            MapPoint endPt = await QueuedTask.Run(() => MapPointBuilder.CreateMapPoint(s.EndPoint.X, s.EndPoint.Y, s.EndPoint.Z * Z_CONVERSION_FACTOR, layerSpatRef));

            //we will be creating three intermediate keyframes for staright segments only if segment length is more than a set threshold
            //the threshold is just a guess and might have to be altered depending upon the path geometry. Should work for most cases though
            MapPoint firstIntPoint = null;
            MapPoint midIntPoint = null;
            MapPoint lastIntPoint = null;

            if (segmentLength >= STRAIGHT_SEGMENT_LENGTH_THRESHOLD)
            {
              //first intermediate point
              firstIntPoint = await CreatePointAlongSegment(startPt, endPt, LINE_CONSTRAINT_FACTOR * segmentLength, layerSpatRef);

              //mid point
              midIntPoint = await CreatePointAlongSegment(startPt, endPt, 0.5 * segmentLength, layerSpatRef);

              //last intermediate point
              lastIntPoint = await CreatePointAlongSegment(startPt, endPt, (1 - LINE_CONSTRAINT_FACTOR) * segmentLength, layerSpatRef);
            }

            //create keyframe at start vertex of path in map space
            double timeSpanValue = accumulatedDuration;
            TimeSpan keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);

            if (segmentLength >= STRAIGHT_SEGMENT_LENGTH_THRESHOLD)
            {
              SetPitchAndHeadingForLine(startPt, firstIntPoint);
            }
            else
            {
              SetPitchAndHeadingForLine(startPt, endPt);
            }

            //ignore rotation for all start vertices (which would also be end vertices of previous segments) EXCEPT for the first vertex of path
            if (num_iterations == 0 || segmentLength < STRAIGHT_SEGMENT_LENGTH_THRESHOLD)
            {
              await CreateCameraKeyframe(mapView, startPt, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);
            }
            else
            {
              await CreateCameraKeyframe(mapView, startPt, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading, true, false);
            }

            if (segmentLength > STRAIGHT_SEGMENT_LENGTH_THRESHOLD)
            {
              //Create a keyframe at PATH_CONSTRAINT_FACTOR distance along the segment from start point
              double distanceAlong = LINE_CONSTRAINT_FACTOR * segmentLength;
              timeSpanValue = accumulatedDuration + LINE_CONSTRAINT_FACTOR * segmentDuration;
              keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);
              SetPitchAndHeadingForLine(firstIntPoint, midIntPoint);
              await CreateCameraKeyframe(mapView, firstIntPoint, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);

              //Create a keyframe at middle of segment
              distanceAlong = 0.5 * segmentLength;
              timeSpanValue = accumulatedDuration + 0.5 * segmentDuration;
              keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);
              SetPitchAndHeadingForLine(midIntPoint, lastIntPoint);
              //await CreateCameraKeyframe(mapView, midIntPoint, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);

              //Create a keyframe at (1 - PATH_CONSTRAINT_FACTOR) distance along the segment from start point
              distanceAlong = (1 - LINE_CONSTRAINT_FACTOR) * segmentLength;
              timeSpanValue = accumulatedDuration + (1 - LINE_CONSTRAINT_FACTOR) * segmentDuration;
              keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);
              SetPitchAndHeadingForLine(lastIntPoint, endPt);
              await CreateCameraKeyframe(mapView, lastIntPoint, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);
            }

            //Create a keyframe at end point of segment only for the end point of last segment
            //Otherwise we will get duplicate keyframes at end of one segment and start of the next one
            if (num_iterations == segmentCount - 1)
            {
              timeSpanValue = accumulatedDuration + segmentDuration;
              keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);

              if (SelectedCameraView == "Face target")
              {
                SetPitchAndHeadingForLine(endPt, TargetPoint);
              }

              await CreateCameraKeyframe(mapView, endPt, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);
            }
          }
          //processing for arcs - create a keyframe every second for arcs
          //we will create a minimum of 5 keyframes along the arc
          else if (s.SegmentType == SegmentType.EllipticArc && segmentDuration > 5)
          {
            EllipticArcSegment ellipArc = s as EllipticArcSegment;
            MapPoint startPt = await QueuedTask.Run(() => MapPointBuilder.CreateMapPoint(s.StartPoint.X, s.StartPoint.Y, s.StartPoint.Z, layerSpatRef));
            MapPoint endPt = await QueuedTask.Run(() => MapPointBuilder.CreateMapPoint(s.EndPoint.X, s.EndPoint.Y, s.EndPoint.Z, layerSpatRef));

            double radius = Math.Sqrt((ellipArc.CenterPoint.X - startPt.X) * (ellipArc.CenterPoint.X - startPt.X) + (ellipArc.CenterPoint.Y - startPt.Y) * (ellipArc.CenterPoint.Y - startPt.Y));
            double angle = ellipArc.CentralAngle;
            MapPoint centerPt = await QueuedTask.Run(() => MapPointBuilder.CreateMapPoint(ellipArc.CenterPoint.X, ellipArc.CenterPoint.Y, (s.StartPoint.Z + s.EndPoint.Z) / 2, layerSpatRef));

            int num_keys = (int)segmentDuration;

            MapPoint firstIntPoint = null;

            //first intermediate keyframe for arc - needed for setting heading for start vertex
            // >2 to account for start and end
            if (num_keys > 2)
            {
              firstIntPoint = await CreatePointAlongArc(startPt, endPt, centerPt, angle / (num_keys - 1), radius, layerSpatRef, ellipArc.IsMinor, ellipArc.IsCounterClockwise);
            }

            //Create keyframe at start vertex of path in map space
            double timeSpanValue = accumulatedDuration;
            TimeSpan keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);

            if (firstIntPoint != null)
            {
              SetPitchAndHeadingForLine(startPt, firstIntPoint);
            }
            else
            {
              SetPitchAndHeadingForLine(startPt, endPt);
            }

            //Ignore rotation for all start vertices EXCEPT for the first vertex of path
            if (num_iterations == 0)
            {
              await CreateCameraKeyframe(mapView, startPt, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);
            }
            else
            {
              await CreateCameraKeyframe(mapView, startPt, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading, true, false);
            }

            //Loop to create intermediate keyframes at each second
            for (int i = 0; i < num_keys - 2; i++)
            {
              MapPoint currentIntPoint = null;
              MapPoint nextIntPoint = null;

              currentIntPoint = await CreatePointAlongArc(startPt, endPt, centerPt, (angle / (num_keys - 1)) * (i + 1), radius, layerSpatRef, ellipArc.IsMinor, ellipArc.IsCounterClockwise);
              if (i < num_keys - 3)
              {
                nextIntPoint = await CreatePointAlongArc(startPt, endPt, centerPt, (angle / (num_keys - 1)) * (i + 2), radius, layerSpatRef, ellipArc.IsMinor, ellipArc.IsCounterClockwise);
              }
              else //for the last intermediate keyframe, heading/pitch has to be determined relative to the end point fo segment
              {
                nextIntPoint = endPt;
              }
              //timeSpanValue = accumulatedDuration + (i + 1) * 1; //at each second
              timeSpanValue = accumulatedDuration + (i + 1) * (segmentDuration / (num_keys - 1));

              keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);
              SetPitchAndHeadingForLine(currentIntPoint, nextIntPoint);
              await CreateCameraKeyframe(mapView, currentIntPoint, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);
            }

            //Create a keyframe at end point of segment only for the end point of last segment
            if (num_iterations == segmentCount - 1)
            {
              timeSpanValue = accumulatedDuration + segmentDuration;
              keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);

              if (SelectedCameraView == "Face target")
              {
                SetPitchAndHeadingForLine(endPt, TargetPoint);
              }

              await CreateCameraKeyframe(mapView, endPt, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);
            }
          }
          //create a minimum of 5 keyframes along the arc
          else if (s.SegmentType == SegmentType.EllipticArc)
          {
            EllipticArcSegment ellipArc = s as EllipticArcSegment;
            MapPoint startPt = await QueuedTask.Run(() => MapPointBuilder.CreateMapPoint(s.StartPoint.X, s.StartPoint.Y, s.StartPoint.Z, layerSpatRef));
            MapPoint endPt = await QueuedTask.Run(() => MapPointBuilder.CreateMapPoint(s.EndPoint.X, s.EndPoint.Y, s.EndPoint.Z, layerSpatRef));

            double radius = Math.Sqrt((ellipArc.CenterPoint.X - startPt.X) * (ellipArc.CenterPoint.X - startPt.X) + (ellipArc.CenterPoint.Y - startPt.Y) * (ellipArc.CenterPoint.Y - startPt.Y));
            double angle = ellipArc.CentralAngle;
            MapPoint centerPt = await QueuedTask.Run(() => MapPointBuilder.CreateMapPoint(ellipArc.CenterPoint.X, ellipArc.CenterPoint.Y, (s.StartPoint.Z + s.EndPoint.Z) / 2, layerSpatRef));

            //we are creating five intermediate keyframes for arcs
            MapPoint firstIntPoint = await CreatePointAlongArc(startPt, endPt, centerPt, angle * ARC_CONSTRAINT_FACTOR, radius, layerSpatRef, ellipArc.IsMinor, ellipArc.IsCounterClockwise);

            MapPoint secondIntPoint = await CreatePointAlongArc(startPt, endPt, centerPt, angle * ARC_CONSTRAINT_FACTOR * 2, radius, layerSpatRef, ellipArc.IsMinor, ellipArc.IsCounterClockwise);

            MapPoint midIntPoint = await CreatePointAlongArc(startPt, endPt, centerPt, angle * 0.5, radius, layerSpatRef, ellipArc.IsMinor, ellipArc.IsCounterClockwise);

            MapPoint secondLastIntPoint = await CreatePointAlongArc(startPt, endPt, centerPt, angle * (1 - ARC_CONSTRAINT_FACTOR * 2), radius, layerSpatRef, ellipArc.IsMinor, ellipArc.IsCounterClockwise);

            MapPoint lastIntPoint = await CreatePointAlongArc(startPt, endPt, centerPt, angle * (1 - ARC_CONSTRAINT_FACTOR), radius, layerSpatRef, ellipArc.IsMinor, ellipArc.IsCounterClockwise);

            //Create keyframe at start vertex of path in map space
            double timeSpanValue = accumulatedDuration;
            TimeSpan keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);
            SetPitchAndHeadingForLine(startPt, firstIntPoint);

            //Ignore rotation for all start vertices EXCEPT for the first vertex of path
            if (num_iterations == 0)
            {
              await CreateCameraKeyframe(mapView, startPt, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);
            }
            else
            {
              await CreateCameraKeyframe(mapView, startPt, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading, true, false);
            }

            //await CreateCameraKeyframe(mapView, startPt, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);

            //Create a keyframe at PATH_CONSTRAINT_FACTOR distance along the segment from start point
            timeSpanValue = accumulatedDuration + ARC_CONSTRAINT_FACTOR * segmentDuration;
            keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);
            SetPitchAndHeadingForLine(firstIntPoint, secondIntPoint);
            await CreateCameraKeyframe(mapView, firstIntPoint, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);

            //Create a keyframe at 2* PATH_CONSTRAINT_FACTOR distance along the segment from start point
            timeSpanValue = accumulatedDuration + ARC_CONSTRAINT_FACTOR * 2 * segmentDuration;
            keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);
            SetPitchAndHeadingForLine(secondIntPoint, midIntPoint);
            await CreateCameraKeyframe(mapView, secondIntPoint, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);

            //Create a keyframe at middle of segment
            timeSpanValue = accumulatedDuration + 0.5 * segmentDuration;
            keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);
            SetPitchAndHeadingForLine(midIntPoint, secondLastIntPoint);
            await CreateCameraKeyframe(mapView, midIntPoint, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);

            //Create a keyframe at (1 - PATH_CONSTRAINT_FACTOR * 2) distance along the segment from start point
            timeSpanValue = accumulatedDuration + (1 - ARC_CONSTRAINT_FACTOR * 2) * segmentDuration;
            keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);
            SetPitchAndHeadingForLine(secondLastIntPoint, lastIntPoint);
            await CreateCameraKeyframe(mapView, secondLastIntPoint, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);

            //Create a keyframe at (1 - PATH_CONSTRAINT_FACTOR) distance along the segment from start point
            timeSpanValue = accumulatedDuration + (1 - ARC_CONSTRAINT_FACTOR) * segmentDuration;
            keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);
            SetPitchAndHeadingForLine(lastIntPoint, endPt);
            await CreateCameraKeyframe(mapView, lastIntPoint, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);

            //Create a keyframe at end point of segment only for the end point of last segment
            if (num_iterations == segmentCount - 1)
            {
              timeSpanValue = accumulatedDuration + segmentDuration;
              keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);

              if (SelectedCameraView == "Face target")
              {
                SetPitchAndHeadingForLine(endPt, TargetPoint);
              }

              await CreateCameraKeyframe(mapView, endPt, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);
            }
          }

          accumulatedDuration += segmentDuration;
          num_iterations++;
        }
      }
    }

    //Use this method to create a keyframe at every n-second of the specified animation duration
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
    public static async Task CreateKeyframes_EveryNSeconds(MapView mapView, SpatialReference layerSpatRef, ProjectionTransformation transformation,
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
                                                    CameraTrack cameraTrack, IEnumerator<ReadOnlySegmentCollection> segments,
                                                    int segmentCount, double pathLength, double keyEveryNSecond = 1)
    {
      double segmentLength = 0;
      int numKeysToCreate = (int)(TotalDuration / keyEveryNSecond); //approximately
      double createKeyAtDist = pathLength / numKeysToCreate;
            
      double skippedDistance = 0;
      double accumulatedDuration = mapView.Map.Animation.Duration.TotalSeconds + ((mapView.Map.Animation.Duration.TotalSeconds > 0) ? ANIMATION_APPEND_TIME : 0); // 0;

      int num_iterations = 0;
      segments.Reset();

      List<MapPoint> pointsForKeyframes = new List<MapPoint>();

      MapPoint pathEndPt = null;

      //process each segment depending upon its type - straight line or arc
      while (segments.MoveNext())
      {
        ReadOnlySegmentCollection seg = segments.Current;

        foreach (Segment s in seg)
        {
          segmentLength = Math.Sqrt((s.EndPoint.X - s.StartPoint.X) * (s.EndPoint.X - s.StartPoint.X) +
                                        (s.EndPoint.Y - s.StartPoint.Y) * (s.EndPoint.Y - s.StartPoint.Y) +
                                          (s.EndPoint.Z - s.StartPoint.Z) * (s.EndPoint.Z - s.StartPoint.Z));
                    
          double segmentDuration = (TotalDuration / pathLength) * segmentLength;

          //straight line segments
          if (s.SegmentType == SegmentType.Line)
          {
            MapPoint startPt = await QueuedTask.Run(() => MapPointBuilder.CreateMapPoint(s.StartPoint.X, s.StartPoint.Y, s.StartPoint.Z, layerSpatRef));
            MapPoint endPt = await QueuedTask.Run(() => MapPointBuilder.CreateMapPoint(s.EndPoint.X, s.EndPoint.Y, s.EndPoint.Z, layerSpatRef));

            //add start of path to points collection
            if (num_iterations == 0)
            {
              pointsForKeyframes.Add(startPt);
            }

            if (num_iterations == segmentCount - 1 || segmentCount == 1)
            {
              pathEndPt = endPt; //store path end pt. This will be the last keyframe.
            }

            double distCoveredAlongSeg = Math.Abs(createKeyAtDist - skippedDistance); //we are accouunting for skipped distances from previous segments

            if (distCoveredAlongSeg < segmentLength)
            {
              MapPoint keyPt = await CreatePointAlongSegment(startPt, endPt, distCoveredAlongSeg, layerSpatRef);
              //add point to collection
              pointsForKeyframes.Add(keyPt);

              //skipped distance is used now, reset to zero
              skippedDistance = 0;

              //are more keyframes possible for this segment
              bool moreKeysPossible = ((segmentLength - distCoveredAlongSeg) >= createKeyAtDist);

              while (moreKeysPossible)
              {
                double keyAtDistAlongSeg = distCoveredAlongSeg + createKeyAtDist;

                keyPt = await CreatePointAlongSegment(startPt, endPt, keyAtDistAlongSeg, layerSpatRef);
                //add point to collection
                pointsForKeyframes.Add(keyPt);

                distCoveredAlongSeg += createKeyAtDist;

                moreKeysPossible = ((segmentLength - distCoveredAlongSeg) > createKeyAtDist);
              }

              //if any segment length left then add to skipped distance
              skippedDistance += (segmentLength - distCoveredAlongSeg);
            }
            else
            {
              //add this segment's length to skipped distance as no keyframe could be created along it
              skippedDistance += segmentLength;
            }
          }
          else if (s.SegmentType == SegmentType.EllipticArc)
          {
            EllipticArcSegment ellipArc = s as EllipticArcSegment;
            MapPoint startPt = await QueuedTask.Run(() => MapPointBuilder.CreateMapPoint(s.StartPoint.X, s.StartPoint.Y, s.StartPoint.Z, layerSpatRef));
            MapPoint endPt = await QueuedTask.Run(() => MapPointBuilder.CreateMapPoint(s.EndPoint.X, s.EndPoint.Y, s.EndPoint.Z, layerSpatRef));

            double radius = Math.Sqrt((ellipArc.CenterPoint.X - startPt.X) * (ellipArc.CenterPoint.X - startPt.X) + (ellipArc.CenterPoint.Y - startPt.Y) * (ellipArc.CenterPoint.Y - startPt.Y));
            double angle = ellipArc.CentralAngle;
            MapPoint centerPt = await QueuedTask.Run(() => MapPointBuilder.CreateMapPoint(ellipArc.CenterPoint.X, ellipArc.CenterPoint.Y, (s.StartPoint.Z + s.EndPoint.Z) / 2, layerSpatRef));

            //add start of path to points collection
            if (num_iterations == 0)
            {
              pointsForKeyframes.Add(startPt);
            }

            if (num_iterations == segmentCount - 1 || segmentCount == 1)
            {
              pathEndPt = endPt; //store path end pt. This will be the last keyframe.
            }

            double distCoveredAlongSeg = Math.Abs(createKeyAtDist - skippedDistance); //we are accouunting for skipped distances from previous segments

            if (distCoveredAlongSeg < segmentLength)
            {
              MapPoint keyPt = await CreatePointAlongArc(startPt, endPt, centerPt, angle * distCoveredAlongSeg / segmentLength, radius, layerSpatRef, ellipArc.IsMinor, ellipArc.IsCounterClockwise);
              //add point to collection
              pointsForKeyframes.Add(keyPt);

              //skipped distance is used now, reset to zero
              skippedDistance = 0;

              //are more keyframes possible for this segment
              bool moreKeysPossible = ((segmentLength - distCoveredAlongSeg) >= createKeyAtDist);

              while (moreKeysPossible)
              {
                double keyAtDistAlongSeg = distCoveredAlongSeg + createKeyAtDist;

                keyPt = await CreatePointAlongArc(startPt, endPt, centerPt, angle * keyAtDistAlongSeg / segmentLength, radius, layerSpatRef, ellipArc.IsMinor, ellipArc.IsCounterClockwise);
                //add point to collection
                pointsForKeyframes.Add(keyPt);

                distCoveredAlongSeg += createKeyAtDist;

                moreKeysPossible = ((segmentLength - distCoveredAlongSeg) > createKeyAtDist);
              }

              //if any segment length left then add to skipped distance
              skippedDistance += (segmentLength - distCoveredAlongSeg);
            }
            else
            {
              //add this segment's length to skipped distance as no keyframe could be created along it
              skippedDistance += segmentLength;
            }

          }

          num_iterations++;
        }
      }

      //now iterate over the points list and create keyframes

      double timeSpanValue = accumulatedDuration;
      TimeSpan keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);

      for (int i = 0; i < pointsForKeyframes.Count; i++)
      {
        MapPoint currentPt = pointsForKeyframes[i];
        MapPoint nextPt = null;

        if (i + 1 < pointsForKeyframes.Count)
        {
          nextPt = pointsForKeyframes[i + 1];
        }
        else
        {
          nextPt = pathEndPt;
        }

        timeSpanValue = i * keyEveryNSecond + accumulatedDuration;
        keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);

        SetPitchAndHeadingForLine(currentPt, nextPt);
        await CreateCameraKeyframe(mapView, currentPt, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);

        if (i == pointsForKeyframes.Count - 1 && skippedDistance > 0)
        {
          keyframeTimespan = TimeSpan.FromSeconds(TotalDuration + accumulatedDuration);
          await CreateCameraKeyframe(mapView, pathEndPt, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);
        }
      }
    }

    //Use this method if you want keyframes ONLY at line vertices. This is good if the line is highly densified.
    //However, you will get sharp turns at corners because there is no attempt to smooth the animation
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
    public static async Task CreateKeyframes_AtVertices(MapView mapView, SpatialReference layerSpatRef, ProjectionTransformation transformation,
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
                                                    CameraTrack cameraTrack, Polyline lineGeom, IEnumerator<ReadOnlySegmentCollection> segments,
                                                    int segmentCount, double pathLength)
    {
      double segmentLength = 0;
      int num_iterations = 0;
      segments.Reset();

      //process each segment depending upon its type - straight line or arc
      while (segments.MoveNext())
      {
        ReadOnlySegmentCollection seg = segments.Current;
        double accumulatedDuration = mapView.Map.Animation.Duration.TotalSeconds + ((mapView.Map.Animation.Duration.TotalSeconds > 0) ? ANIMATION_APPEND_TIME : 0); // 0;

        foreach (Segment s in seg)
        {
          segmentLength = Math.Sqrt((s.EndPoint.X - s.StartPoint.X) * (s.EndPoint.X - s.StartPoint.X) +
                                        (s.EndPoint.Y - s.StartPoint.Y) * (s.EndPoint.Y - s.StartPoint.Y) +
                                          (s.EndPoint.Z - s.StartPoint.Z) * (s.EndPoint.Z - s.StartPoint.Z));

          double segmentDuration = (TotalDuration / pathLength) * segmentLength;

          MapPoint startPt = await QueuedTask.Run(() => MapPointBuilder.CreateMapPoint(s.StartPoint.X, s.StartPoint.Y, s.StartPoint.Z, layerSpatRef));
          MapPoint endPt = await QueuedTask.Run(() => MapPointBuilder.CreateMapPoint(s.EndPoint.X, s.EndPoint.Y, s.EndPoint.Z, layerSpatRef));

          //create keyframe at start vertex of path in map space
          double timeSpanValue = accumulatedDuration;
          TimeSpan keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);
          SetPitchAndHeadingForLine(startPt, endPt);
          await CreateCameraKeyframe(mapView, startPt, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);

          //Create a keyframe at end point of segment only for the end point of last segment
          //Otherwise we will get duplicate keyframes at end of one segment and start of the next one
          if (num_iterations == segmentCount - 1)
          {
            timeSpanValue = accumulatedDuration + segmentDuration;
            keyframeTimespan = TimeSpan.FromSeconds(timeSpanValue);

            if (SelectedCameraView == "Face target")
            {
              SetPitchAndHeadingForLine(endPt, TargetPoint);
            }

            await CreateCameraKeyframe(mapView, endPt, transformation, cameraTrack, keyframeTimespan, _keyframePitch, _keyframeHeading);
          }

          accumulatedDuration += segmentDuration;
          num_iterations++;
        }
      }
    }

    private static async Task<MapPoint> CreatePointAlongSegment(MapPoint startPt, MapPoint endPt, double distanceFromStartPoint, SpatialReference spatRef)
    {
      System.Windows.Media.Media3D.Point3D fromPt = new System.Windows.Media.Media3D.Point3D(startPt.X, startPt.Y, startPt.Z);
      System.Windows.Media.Media3D.Point3D toPt = new System.Windows.Media.Media3D.Point3D(endPt.X, endPt.Y, endPt.Z);
      System.Windows.Media.Media3D.Vector3D lineVec = new System.Windows.Media.Media3D.Vector3D(toPt.X - fromPt.X, toPt.Y - fromPt.Y, toPt.Z - fromPt.Z);

      lineVec.Normalize();

      System.Windows.Media.Media3D.Point3D ptAlong = new System.Windows.Media.Media3D.Point3D(fromPt.X + distanceFromStartPoint * (lineVec.X),
                                                          fromPt.Y + distanceFromStartPoint * (lineVec.Y),
                                                          fromPt.Z + distanceFromStartPoint * (lineVec.Z));

      MapPoint intermediateKeyframePoint = await QueuedTask.Run(() => MapPointBuilder.CreateMapPoint(ptAlong.X, ptAlong.Y, ptAlong.Z, spatRef));
      return intermediateKeyframePoint;
    }
    private static async Task<MapPoint> CreatePointAlongArc(MapPoint startPt, MapPoint endPt, MapPoint centerPt, double angle, double radius, SpatialReference spatRef, bool arcIsMinor, bool arcIsCounterClockwise)
    {
      System.Windows.Media.Media3D.Vector3D start = new System.Windows.Media.Media3D.Vector3D(startPt.X - centerPt.X, startPt.Y - centerPt.Y, startPt.Z - centerPt.Z);
      System.Windows.Media.Media3D.Vector3D end = new System.Windows.Media.Media3D.Vector3D(endPt.X - centerPt.X, endPt.Y - centerPt.Y, endPt.Z - centerPt.Z);

      System.Windows.Media.Media3D.Vector3D normalOfPlane = new System.Windows.Media.Media3D.Vector3D();
      normalOfPlane = System.Windows.Media.Media3D.Vector3D.CrossProduct(start, end);

      //Two ortho vectors: orthoVec and start
      System.Windows.Media.Media3D.Vector3D orthoVec = new System.Windows.Media.Media3D.Vector3D();
      orthoVec = System.Windows.Media.Media3D.Vector3D.CrossProduct(normalOfPlane, start);

      //If this is not done then half of the keyframes for S-shaped curve are not on the curve
      if (arcIsMinor && !arcIsCounterClockwise)
        orthoVec.Negate();

      //Normalize
      start.Normalize();
      orthoVec.Normalize();

      System.Windows.Media.Media3D.Vector3D ptAlong = new System.Windows.Media.Media3D.Vector3D();
      ptAlong = radius * Math.Cos(angle) * start + radius * Math.Sin(angle) * orthoVec;

      MapPoint intermediateKeyframePoint = await QueuedTask.Run(() => MapPointBuilder.CreateMapPoint(ptAlong.X + centerPt.X, ptAlong.Y + centerPt.Y, ptAlong.Z + centerPt.Z, spatRef));
      return intermediateKeyframePoint;
    }
    private static async Task CreateCameraKeyframe(MapView mapView, MapPoint orig_cameraPoint, ProjectionTransformation transformation,
                                                             CameraTrack cameraTrack, TimeSpan currentTimespanValue, double pitch, double heading, bool ignoreRotation = false, bool ignoreTranslation = false)
    {
      await QueuedTask.Run(() =>
      {
        Keyframe keyFrame = null;
        MapPoint projected_cameraPoint = (MapPoint)GeometryEngine.Instance.ProjectEx(orig_cameraPoint, transformation);

        if (mapView.ViewingMode == MapViewingMode.Map)
        {
          var camera = new Camera(projected_cameraPoint.X, projected_cameraPoint.Y, CameraZOffset, heading, null, CameraViewpoint.LookAt);
          keyFrame = cameraTrack.CreateKeyframe(camera, currentTimespanValue, AnimationTransition.FixedArc, .5);
        }
        else
        {
          var camera = new Camera(projected_cameraPoint.X, projected_cameraPoint.Y, (projected_cameraPoint.Z + CameraZOffset), pitch, heading, null, CameraViewpoint.LookAt);
          keyFrame = cameraTrack.CreateKeyframe(camera, currentTimespanValue, AnimationTransition.FixedArc, .5);
        }

        if (ignoreRotation)
        {
          CameraKeyframe camKey = keyFrame as CameraKeyframe;
          camKey.HeadingTransition = AnimationTransition.None;
          camKey.RollTransition = AnimationTransition.None;
          camKey.PitchTransition = AnimationTransition.None;
        }
        if (ignoreTranslation)
        {
          CameraKeyframe camKey = keyFrame as CameraKeyframe;
          camKey.XTransition = AnimationTransition.None;
          camKey.YTransition = AnimationTransition.None;
          camKey.ZTransition = AnimationTransition.None;
        }
      });
    }

    private static void SetPitchAndHeadingForLine(MapPoint startPt, MapPoint endPt)
    {
      if (SelectedCameraView == "Top down")
      {
        _keyframeHeading = CalculateHeading(startPt, endPt);
        _keyframePitch = -90;
      }
      else if (SelectedCameraView == "Top down - face north")
      {
        _keyframeHeading = 0;
        _keyframePitch = -90;
      }
      else if (SelectedCameraView == "Custom pitch")
      {
        _keyframeHeading = CalculateHeading(startPt, endPt);
        _keyframePitch = CustomPitch;
      }
      else if (SelectedCameraView == "View along" || SelectedCameraView == "Face backward")
      {
        _keyframeHeading = CalculateHeading(startPt, endPt);
        _keyframePitch = CalculatePitch(startPt, endPt);
      }
      else if (SelectedCameraView == "Face target")
      {
        _keyframeHeading = CalculateHeading(startPt, TargetPoint);
        _keyframePitch = CalculatePitch(startPt, TargetPoint);
      }
    }
    private static double CalculateHeading(MapPoint startPt, MapPoint endPt)
    {
      double dx, dy, dz, angle, heading;

      if (SelectedCameraView == "Top down - face north")
      {
        heading = 0;
      }
      else
      {
        dx = endPt.X - startPt.X;
        dy = endPt.Y - startPt.Y;

        //need to apply z-conversion factor to target Z
        dz = (SelectedCameraView == "Face target") ? endPt.Z * Z_CONVERSION_FACTOR - startPt.Z : endPt.Z - startPt.Z;

        angle = Math.Atan2(dy, dx);
        heading = 180 + (90 + angle * 180 / Math.PI);

        if (SelectedCameraView == "Face backward") { heading = heading - 180; }
      }

      return heading;
    }
    private static double CalculatePitch(MapPoint startPt, MapPoint endPt)
    {
      double dx, dy, dz, pitchForLookingAtTarget;
      dx = startPt.X - endPt.X;
      dy = startPt.Y - endPt.Y;
      //dz = startPt.Z - endPt.Z;

      //need to apply z-conversion factor to target Z
      dz = (SelectedCameraView == "Face target") ? startPt.Z - endPt.Z * Z_CONVERSION_FACTOR : startPt.Z - endPt.Z;

      //try dividing by 0.00017 unit conversion factor if units = degree
      SpatialReference spatRef = startPt.SpatialReference;
      if (spatRef.Unit.Name == "Degree")
      {
        dx = dx * 111000;
        dy = dy * 111000;
      }

      double test = dz / Math.Sqrt(dx * dx + dy * dy + dz * dz);
      pitchForLookingAtTarget = -(90 - Math.Acos(test) * 180 / Math.PI);
      return pitchForLookingAtTarget;
    }

    #endregion
  }
}

 

标签:沿线,double,Pro,await,MapPoint,ArcGIS,startPt,StartPoint,endPt
From: https://www.cnblogs.com/gisoracle/p/16711016.html

相关文章