/// <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.Instance.ProjectEx(line, transformation) as Polyline; } else { var transformation = ProjectionTransformation.Create(line.SpatialReference, mapView.Map.SpatialReference, line.Extent); line = GeometryEngine.Instance.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.Instance.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.Instance.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 = PolylineBuilderEx.CreatePolyline(new List <MapPoint>() { line.Points[i - 1], line.Points[i] }, AttributeFlags.HasZ, 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; } })); }
/// <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 = MapPointBuilderEx.CreateMapPoint(camera.X, camera.Y, camera.SpatialReference); var radius = GeometryEngine.Instance.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 = MapPointBuilderEx.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.Instance.ProjectEx(point, transformation) as MapPoint; } //Create an ellipse around the center point. var parameter = new GeodesicEllipseParameter() { Center = point.Coordinate2D, SemiAxis1Length = radius, SemiAxis2Length = radius, AxisDirection = radian, LinearUnit = LinearUnit.Meters, OutGeometryType = GeometryType.Polyline, VertexCount = 36 }; var ellipse = GeometryEngine.Instance.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); } })); }
private async Task RedrawConeAsync() { await QueuedTask.Run(() => { GlobeSpotter globeSpotter = GlobeSpotter.Current; if ((globeSpotter.InsideScale()) && (!_mapPoint.IsEmpty) && (Color != null)) { var thisColor = (SystCol)Color; MapView thisView = MapView.Active; Map map = thisView.Map; SpatialReference mapSpat = map.SpatialReference; SpatialReference mapPointSpat = _mapPoint.SpatialReference; ProjectionTransformation projection = ProjectionTransformation.Create(mapPointSpat, mapSpat); _mapPoint = GeometryEngine.Instance.ProjectEx(_mapPoint, projection) as MapPoint; WinPoint point = thisView.MapToScreen(_mapPoint); double angleh = (_hFov *Math.PI) / 360; double angle = (((270 + _angle) % 360) * Math.PI) / 180; double angle1 = angle - angleh; double angle2 = angle + angleh; double x = point.X; double y = point.Y; double size = Size / 2; WinPoint screenPoint1 = new WinPoint((x + (size * Math.Cos(angle1))), (y + (size * Math.Sin(angle1)))); WinPoint screenPoint2 = new WinPoint((x + (size * Math.Cos(angle2))), (y + (size * Math.Sin(angle2)))); MapPoint point1 = thisView.ScreenToMap(screenPoint1); MapPoint point2 = thisView.ScreenToMap(screenPoint2); IList <MapPoint> polygonPointList = new List <MapPoint>(); polygonPointList.Add(_mapPoint); polygonPointList.Add(point1); polygonPointList.Add(point2); polygonPointList.Add(_mapPoint); Polygon polygon = PolygonBuilder.CreatePolygon(polygonPointList); Color colorPolygon = SystCol.FromArgb(_blinking ? BlinkAlpha : NormalAlpha, thisColor); CIMColor cimColorPolygon = ColorFactory.Instance.CreateColor(colorPolygon); CIMPolygonSymbol polygonSymbol = SymbolFactory.Instance.DefaultPolygonSymbol; polygonSymbol.SetColor(cimColorPolygon); polygonSymbol.SetOutlineColor(null); CIMSymbolReference polygonSymbolReference = polygonSymbol.MakeSymbolReference(); IDisposable disposePolygon = thisView.AddOverlay(polygon, polygonSymbolReference); IList <MapPoint> linePointList = new List <MapPoint>(); linePointList.Add(point1); linePointList.Add(_mapPoint); linePointList.Add(point2); Polyline polyline = PolylineBuilder.CreatePolyline(linePointList); Color colorLine = _active ? SystCol.Yellow : SystCol.Gray; CIMColor cimColorLine = ColorFactory.Instance.CreateColor(colorLine); CIMLineSymbol cimLineSymbol = SymbolFactory.Instance.DefaultLineSymbol; cimLineSymbol.SetColor(cimColorLine); cimLineSymbol.SetSize(_blinking ? BorderSizeBlinking : BorderSize); CIMSymbolReference lineSymbolReference = cimLineSymbol.MakeSymbolReference(); IDisposable disposePolyLine = thisView.AddOverlay(polyline, lineSymbolReference); _disposePolygon?.Dispose(); _disposePolygon = disposePolygon; _disposePolyLine?.Dispose(); _disposePolyLine = disposePolyLine; if (_blinking) { var blinkEvent = new AutoResetEvent(true); var blinkTimerCallBack = new TimerCallback(ResetBlinking); _blinkTimer = new Timer(blinkTimerCallBack, blinkEvent, BlinkTime, -1); } } else { _disposePolygon?.Dispose(); _disposePolyLine?.Dispose(); } }); }
/* * protected async override void OnUpdate() * { * Cursor nowCursor = Cursor; * Cursor = _containsFeatures ? Cursors.Arrow : _thisCursor; * * if (nowCursor != Cursor) * { * await FrameworkApplication.SetCurrentToolAsync("esri_mapping_exploreTool"); * await FrameworkApplication.SetCurrentToolAsync("StreetSmartArcGISPro_openImageTool"); * } * * base.OnUpdate(); * } * * protected override async void OnToolMouseMove(MapViewMouseEventArgs e) * { * await QueuedTask.Run(() => * { * var constants = ConstantsRecordingLayer.Instance; * double size = constants.SizeLayer; * double halfSize = size / 2; * MapView activeView = MapView.Active; * * WinPoint clientPoint = e.ClientPoint; * WinPoint pointScreen = activeView.ClientToScreen(clientPoint); * double x = pointScreen.X; * double y = pointScreen.Y; * WinPoint minPoint = new WinPoint(x - halfSize, y - halfSize); * WinPoint maxPoint = new WinPoint(x + halfSize, y + halfSize); * MapPoint minPoint1 = activeView.ScreenToMap(minPoint); * MapPoint maxPoint1 = activeView.ScreenToMap(maxPoint); * Envelope envelope = EnvelopeBuilder.CreateEnvelope(minPoint1, maxPoint1, minPoint1.SpatialReference); * var features = MapView.Active?.GetFeatures(envelope); * _containsFeatures = (features != null) && (features.Count >= 1); * }); * * base.OnToolMouseMove(e); * } */ protected override Task <bool> OnSketchCompleteAsync(Geometry geometry) { return(QueuedTask.Run(() => { MapPoint point = geometry as MapPoint; MapView activeView = MapView.Active; if (point != null && activeView != null) { var constants = ConstantsRecordingLayer.Instance; double size = constants.SizeLayer; double halfSize = size / 2; SpatialReference pointSpatialReference = point.SpatialReference; var pointScreen = activeView.MapToScreen(point); double x = pointScreen.X; double y = pointScreen.Y; WinPoint pointScreenMin = new WinPoint(x - halfSize, y - halfSize); WinPoint pointScreenMax = new WinPoint(x + halfSize, y + halfSize); var pointMapMin = activeView.ScreenToMap(pointScreenMin); var pointMapMax = activeView.ScreenToMap(pointScreenMax); Envelope envelope = EnvelopeBuilder.CreateEnvelope(pointMapMin, pointMapMax, pointSpatialReference); var features = activeView.GetFeatures(envelope); ModuleStreetSmart streetSmart = ModuleStreetSmart.Current; CycloMediaGroupLayer groupLayer = streetSmart?.CycloMediaGroupLayer; if (features != null && groupLayer != null) { _nearest = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl); if (_nearest) { Settings settings = Settings.Instance; MySpatialReference cycloCoordSystem = settings.CycloramaViewerCoordinateSystem; if (cycloCoordSystem != null) { SpatialReference cycloSpatialReference = cycloCoordSystem.ArcGisSpatialReference ?? cycloCoordSystem.CreateArcGisSpatialReferenceAsync().Result; if (pointSpatialReference.Wkid != cycloSpatialReference.Wkid) { ProjectionTransformation projection = ProjectionTransformation.Create(pointSpatialReference, cycloSpatialReference); point = GeometryEngine.Instance.ProjectEx(point, projection) as MapPoint; } if (point != null) { CultureInfo ci = CultureInfo.InvariantCulture; _location = string.Format(ci, "{0},{1}", point.X, point.Y); if (!streetSmart.InsideScale()) { double minimumScale = ConstantsRecordingLayer.Instance.MinimumScale; double scale = minimumScale / 2; Camera camera = new Camera(point.X, point.Y, scale, 0.0); MapView.Active?.ZoomTo(camera); } } } } else { foreach (var feature in features) { Layer layer = feature.Key; CycloMediaLayer cycloMediaLayer = groupLayer.GetLayer(layer); if (cycloMediaLayer != null) { foreach (long uid in feature.Value) { Recording recording = cycloMediaLayer.GetRecordingAsync(uid).Result; if (recording.IsAuthorized == null || (bool)recording.IsAuthorized) { _location = recording.ImageId; } } } } } } } return true; })); }
public static async Task CreateKeyframes() { 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(); Feature selectedFtr = result.Current as Feature; return(selectedFtr.GetShape() as Polyline); } 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); } }