コード例 #1
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.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;
                }
            }));
        }
コード例 #2
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 = 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);
                }
            }));
        }
コード例 #3
0
        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();
                }
            });
        }
コード例 #4
0
/*
 *  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);
            }
        }