コード例 #1
0
        /// <summary>
        /// Transforms an XYZ direction in the <see cref="MapRenderer"/>'s local space to a direction in Mercator space.
        /// Uses the specified zoom level rather than the <see cref="MapRenderer"/>'s zoom level for the transformation.
        /// </summary>
        public static MercatorCoordinate TransformLocalDirectionToMercator(Vector3 directionInLocalSpace, double zoomLevel)
        {
            var mercatorMapSizeInLocalSpace = Math.Pow(2, zoomLevel - 1);
            var directionInMercator         = new MercatorCoordinate(directionInLocalSpace.x, directionInLocalSpace.z) / mercatorMapSizeInLocalSpace;

            return(directionInMercator);
        }
コード例 #2
0
        /// <summary>
        /// Initializes values for the bow animation.
        /// </summary>
        private void ComputeBowAnimationInitialValues(
            double p,
            double v,
            out double u0,
            out double u1,
            out double w0,
            out double w1,
            out double r0,
            out double r1,
            out double S,
            out double animationTime)
        {
            u0 = 0;
            u1 = MercatorCoordinate.Distance(_endMercatorCenter, _startMercatorCenter);

            w0 = 1.0 / Math.Pow(2, _startZoomLevel - 1);
            w1 = 1.0 / Math.Pow(2, _endZoomLevel - 1);

            if (u0 == u1)
            {
                S = Math.Abs(Math.Log(w1 / w0)) / p;

                // r0 and r1 are unused.
                r0 = 0;
                r1 = 1;
            }
            else
            {
                r0 = R(0, w0, w1, u0, u1, p);
                r1 = R(1, w0, w1, u0, u1, p);
                S  = (r1 - r0) / p;
            }

            animationTime = S / v;
        }
コード例 #3
0
        /// <inheritdoc cref="ToMercatorCoordinates(Size, bool)"/>
        /// <param name="number"><see cref="Number"/> to convert</param>
        /// <param name="tileSize"></param>
        /// <param name="tmsCompatible"></param>
        public static (MercatorCoordinate minCoordinate, MercatorCoordinate maxCoordinate) ToMercatorCoordinates(
            Number number, Size tileSize, bool tmsCompatible)
        {
            #region Preconditions checks

            if (number == null)
            {
                throw new ArgumentNullException(nameof(number));
            }
            if (tileSize == null)
            {
                throw new ArgumentNullException(nameof(tileSize));
            }

            #endregion

            if (!tmsCompatible)
            {
                number = Flip(number);
            }

            PixelCoordinate minPixelCoordinate = new PixelCoordinate(number.X * tileSize.Width,
                                                                     number.Y * tileSize.Height);
            PixelCoordinate maxPixelCoordinate = new PixelCoordinate((number.X + 1) * tileSize.Width,
                                                                     (number.Y + 1) * tileSize.Height);
            MercatorCoordinate minCoordinate = minPixelCoordinate.ToMercatorCoordinate(CoordinateSystem.Epsg3857,
                                                                                       number.Z, tileSize);
            MercatorCoordinate maxCoordinate = maxPixelCoordinate.ToMercatorCoordinate(CoordinateSystem.Epsg3857,
                                                                                       number.Z, tileSize);

            return(minCoordinate, maxCoordinate);
        }
コード例 #4
0
 public void GetProperties() => Assert.DoesNotThrow(() =>
 {
     MercatorCoordinate coord =
         new MercatorCoordinate(Locations.TokyoMercatorLongitude, Locations.TokyoMercatorLatitude);
     double val = MercatorCoordinate.MinPossibleLonValue;
     val        = MercatorCoordinate.MaxPossibleLonValue;
     val        = MercatorCoordinate.MinPossibleLatValue;
     val        = MercatorCoordinate.MaxPossibleLatValue;
     val        = coord.X;
     val        = coord.Y;
 });
コード例 #5
0
        /// <inheritdoc/>
        public void Initialize(
            MapRendererBase mapRenderer,
            MapScene mapScene,
            float animationTimeScale,
            MapSceneAnimationKind mapSceneAnimationKind)
        {
            if (mapRenderer == null)
            {
                throw new ArgumentNullException(nameof(mapRenderer));
            }

            if (mapScene == null)
            {
                throw new ArgumentNullException(nameof(mapScene));
            }

            _runningTime = 0;

            _startMercatorCenter = mapRenderer.Center.ToMercatorCoordinate();
            _startZoomLevel      = mapRenderer.ZoomLevel;
            mapScene.GetLocationAndZoomLevel(out var endLocation, out _endZoomLevel);
            _endZoomLevel          = _endZoomLevel < mapRenderer.MinimumZoomLevel ? mapRenderer.MinimumZoomLevel : _endZoomLevel;
            _endZoomLevel          = _endZoomLevel > mapRenderer.MaximumZoomLevel ? mapRenderer.MaximumZoomLevel : _endZoomLevel;
            _endMercatorCenter     = endLocation.ToMercatorCoordinate();
            _startHeightInMercator = ZoomLevelToMercatorAltitude(_startZoomLevel);
            _endHeightInMercator   = ZoomLevelToMercatorAltitude(_endZoomLevel);

            // Determine if we should use a bow animation or a linear animation.
            // Simple rule for now: If the destination is already visible, use linear; otherwise, bow.
            _isLinearAnimation =
                mapSceneAnimationKind == MapSceneAnimationKind.Linear ||
                mapSceneAnimationKind == MapSceneAnimationKind.SmoothLinear ||
                mapRenderer.Bounds.Intersects(endLocation);
            _isSmoothed = mapSceneAnimationKind == MapSceneAnimationKind.SmoothLinear;

            // While linear animation doesn't follow the same code path as the bow animation, we can still use this function to compute
            // a reasonable duration for the linear animation.
            ComputeBowAnimationInitialValues(
                P,
                V * animationTimeScale,
                out _u0,
                out _u1,
                out _w0,
                out _, // w1
                out _r0,
                out _, // r1
                out _S,
                out _animationTime);

            // Tweaking the resulting animation time to speed up slower animations and slow down the shorter animation.
            _animationTime /= 6.0;                                 // Normalize.
            _animationTime  = Math.Pow(_animationTime, 1.0 / 3.0); // Rescale.
            _animationTime *= 6.0;                                 // Convert back to original range.
        }
コード例 #6
0
        public void ToGeoCoordinatesMercator()
        {
            GeoCoordinate minCoordinate = null;
            GeoCoordinate maxCoordinate = null;

            Assert.DoesNotThrow(() => (minCoordinate, maxCoordinate) = Locations.TokyoMercatorNtmsNumber
                                                                       .ToGeoCoordinates(Cs3857, Tile.DefaultSize, false));

            MercatorCoordinate min = Coordinate.Round((MercatorCoordinate)minCoordinate, 2);
            MercatorCoordinate max = Coordinate.Round((MercatorCoordinate)maxCoordinate, 2);

            Assert.True(min == Locations.TokyoMercatorMin && max == Locations.TokyoMercatorMax);
        }
コード例 #7
0
        public void ToMercatorCoordinatesNormal()
        {
            MercatorCoordinate minCoordinate = null;
            MercatorCoordinate maxCoordinate = null;

            Assert.DoesNotThrow(() => (minCoordinate, maxCoordinate) = Locations.TokyoMercatorNtmsNumber
                                                                       .ToMercatorCoordinates(Tile.DefaultSize, false));

            MercatorCoordinate min = Coordinate.Round(minCoordinate, 2);
            MercatorCoordinate max = Coordinate.Round(maxCoordinate, 2);

            Assert.True(min == Locations.TokyoMercatorMin && max == Locations.TokyoMercatorMax);
        }
コード例 #8
0
    public void OnPointerDown(MixedRealityPointerEventData eventData)
    {
        if (_isFocused &&
            CoreServices.InputSystem.FocusProvider.TryGetFocusDetails(eventData.Pointer, out FocusDetails focusDetails))
        {
            _pointer = eventData.Pointer;
            _targetPointInLocalSpace = focusDetails.PointLocalSpace;
            _targetPointInMercator   =
                _mapRenderer.TransformLocalPointToMercatorWithAltitude(
                    _targetPointInLocalSpace,
                    out _startingAltitudeInMeters,
                    out _);
            _currentPointInLocalSpace = _targetPointInLocalSpace;

            eventData.Use();
        }
    }
コード例 #9
0
    public void OnPointerDown(MixedRealityPointerEventData eventData)
    {
        if (CoreServices.InputSystem.FocusProvider.TryGetFocusDetails(eventData.Pointer, out FocusDetails focusDetails) &&
            focusDetails.Object == gameObject)
        {
            _panningPointer            = eventData.Pointer;
            _startingPointInLocalSpace = focusDetails.PointLocalSpace;
            _startingPointInMercator   =
                _mapRenderer.TransformLocalPointToMercatorWithAltitude(
                    _startingPointInLocalSpace,
                    out _startingAltitudeInMeters,
                    out _startingMercatorScale);
            _currentPointInLocalSpace    = _startingPointInLocalSpace;
            _startingMapCenterInMercator = _mapRenderer.Center.ToMercatorCoordinate();

            eventData.Use();
        }
    }
コード例 #10
0
    public void OnPointerDown(MixedRealityPointerEventData eventData)
    {
        if (_isFocused &&
            CoreServices.InputSystem.FocusProvider.TryGetFocusDetails(eventData.Pointer, out var focusDetails) &&
            focusDetails.Object == gameObject)
        {
            _pointer             = eventData.Pointer;
            _lastPointerDownTime = Time.time;

            _targetPointInLocalSpace = focusDetails.PointLocalSpace;
            _targetPointInMercator   =
                MapRenderer.TransformLocalPointToMercatorWithAltitude(
                    _targetPointInLocalSpace,
                    out _targetAltitudeInMeters,
                    out _);
            _currentPointInLocalSpace  = _targetPointInLocalSpace;
            _smoothedPointInLocalSpace = _targetPointInLocalSpace;

            eventData.Use();
        }
    }
コード例 #11
0
        private void OnMouseDown()
        {
            if (!isActiveAndEnabled || IsPointerOverGameObject())
            {
                return;
            }

            var mousePosition = Input.mousePosition;

            if (IsPixelOverCamera(mousePosition))
            {
                var ray = Camera.ScreenPointToRay(mousePosition);
                if (MapRenderer.Raycast(ray, out var hitInfo))
                {
                    _isMouseDown = true;
                    _interactionTargetCoordinate = hitInfo.Location.LatLon.ToMercatorCoordinate();
                    _interactionTargetAltitude   = hitInfo.Location.AltitudeInMeters;
                    _initialMousePosition        = mousePosition;
                    _lastMouseDownTime           = Time.time;
                }
            }
        }
コード例 #12
0
 public void ResolutionNormal() => Assert.DoesNotThrow(() =>
 {
     double res = MercatorCoordinate.Resolution(10, Tile.DefaultSize);
 });
コード例 #13
0
 public void ResolutionNullTileSize() => Assert.Throws <ArgumentNullException>(() =>
 {
     double res = MercatorCoordinate.Resolution(10, null);
 });
コード例 #14
0
 public void ResolutionNotSquareTileSize() => Assert.Throws <ArgumentException>(() =>
 {
     Size size  = new Size(10, 20);
     double res = MercatorCoordinate.Resolution(10, size);
 });
コード例 #15
0
        private void OnSceneGUI()
        {
            var mapRenderer = target as MapRenderer;

            if (mapRenderer == null)
            {
                return;
            }

            if (Event.current.modifiers == EventModifiers.Control)
            {
                // Turn off the translation tool.
                Tools.hidden = true;

                // Change the cursor to make it obvious you can move the map around. Make the rect big enough to cover the scene view.
                EditorGUIUtility.AddCursorRect(new Rect(0, 0, 999999, 999999), MouseCursor.MoveArrow);

                var currentEvent = Event.current;
                if (currentEvent.type == EventType.Layout)
                {
                    // Adding a control ID disables left-click-and-drag from creating a selection rect, rather than translating the map.
                    int controlID = GUIUtility.GetControlID(ControlIdHint, FocusType.Passive);
                    HandleUtility.AddDefaultControl(controlID);
                }
                else if (currentEvent.type == EventType.ScrollWheel)
                {
                    // Zoom map based on scroll wheel.
                    var ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
                    if (mapRenderer.Raycast(ray, out var hitInfo))
                    {
                        Undo.RecordObject(mapRenderer, "Change ZoomLevel.");
                        var delta = -Event.current.delta;
                        mapRenderer.ZoomLevel += delta.y / 50;
                        currentEvent.Use();

                        EditorApplication.QueuePlayerLoopUpdate();
                    }
                }
                else if (currentEvent.type == EventType.MouseDown)
                {
                    // Begin panning if the mouse ray hits the map.
                    var ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
                    if (mapRenderer.Raycast(ray, out var hitInfo))
                    {
                        if (Event.current.button == 1) // right-click
                        {
                            var menu = new GenericMenu();
                            menu.AddItem(
                                new GUIContent("Add MapPin"),
                                false,
                                (object o) =>
                            {
                                var location                = hitInfo.Location;
                                var gameObject              = new GameObject();
                                gameObject.name             = "MapPin";
                                gameObject.transform.parent = mapRenderer.transform;
                                var mapPin               = gameObject.AddComponent <MapPin>();
                                mapPin.Location          = location.LatLon;
                                mapPin.Altitude          = location.AltitudeInMeters;
                                mapPin.AltitudeReference = AltitudeReference.Ellipsoid;
                                return;
                            },
                                0);
                            menu.ShowAsContext();
                            _isDragging = false;
                        }
                        else
                        {
                            _startingHitPointInWorldSpace = hitInfo.Point;
                            _startingCenterInMercator     = mapRenderer.Center.ToMercatorCoordinate();
                            _isDragging = true;
                        }
                        currentEvent.Use();
                    }
                }
                else if (_isDragging && currentEvent.type == EventType.MouseDrag)
                {
                    // Update center
                    var ray   = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
                    var plane = new Plane(mapRenderer.transform.up, _startingHitPointInWorldSpace);
                    if (plane.Raycast(ray, out var enter))
                    {
                        var updatedHitPointInWorldSpace = ray.GetPoint(enter);
                        var newDeltaInWorldSpace        = updatedHitPointInWorldSpace - _startingHitPointInWorldSpace;
                        var newDeltaInLocalSpace        = mapRenderer.transform.worldToLocalMatrix * newDeltaInWorldSpace;
                        var newDeltaInMercator          = new MercatorCoordinate(newDeltaInLocalSpace.x, newDeltaInLocalSpace.z) / Math.Pow(2, mapRenderer.ZoomLevel - 1);
                        var newCenter = (_startingCenterInMercator - newDeltaInMercator).ToLatLon();

                        Undo.RecordObject(mapRenderer, "Change Center.");
                        mapRenderer.Center = newCenter;

                        EditorApplication.QueuePlayerLoopUpdate();
                    }

                    currentEvent.Use();
                }
                else if (_isDragging && currentEvent.type == EventType.MouseUp)
                {
                    _isDragging = false;

                    currentEvent.Use();
                }
            }
            else
            {
                Tools.hidden = false;
            }
        }
コード例 #16
0
 public void CreateMercatorCoordinateNormal() => Assert.DoesNotThrow(() =>
 {
     MercatorCoordinate coord =
         new MercatorCoordinate(Locations.TokyoMercatorLongitude, Locations.TokyoMercatorLatitude);
 });
コード例 #17
0
        private void OnSceneGUI()
        {
            // If childed to a MapRenderer, don't use the default tools. Use a tool that can update the MapPin's location.
            var mapPin      = (MapPin)target;
            var mapRenderer = mapPin.transform.parent?.GetComponent <MapRenderer>();

            if (mapRenderer != null)
            {
                var transform = mapPin.transform;

                Tools.hidden = true;

                var controlId = GUIUtility.GetControlID("MapPinEditorHandle".GetHashCode(), FocusType.Passive);
                var size      = 0.025f;

                switch (Event.current.GetTypeForControl(controlId))
                {
                case EventType.Layout:
                    Handles.RectangleHandleCap(
                        controlId,
                        _isDragging ? new Vector3(transform.position.x, (float)_mouseDownMapPinPositionInMercatorSpace.Y, transform.position.z) : transform.position,
                        transform.rotation * Quaternion.LookRotation(Vector3.up),
                        size,
                        EventType.Layout);
                    break;

                case EventType.MouseMove:
                    if (HandleUtility.nearestControl == controlId)
                    {
                        _isHovered = true;
                        Event.current.Use();
                    }
                    else
                    {
                        _isHovered = false;
                    }
                    break;

                case EventType.MouseDown:
                    if (HandleUtility.nearestControl == controlId)
                    {
                        var ray   = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
                        var plane = new Plane(transform.up, transform.position);
                        if (plane.Raycast(ray, out var enter))
                        {
                            // Respond to a press on this handle. Drag starts automatically.
                            _mouseDownMapPinPlanePositionInMapLocalSpace = mapRenderer.transform.worldToLocalMatrix * ray.GetPoint(enter);
                            _mouseDownMapPinPositionInMercatorSpace      = mapPin.Location.ToMercatorCoordinate();
                            _isDragging           = true;
                            GUIUtility.hotControl = controlId;
                            Event.current.Use();
                        }
                    }
                    break;

                case EventType.MouseUp:
                    if (GUIUtility.hotControl == controlId)
                    {
                        // Respond to a release on this handle. Drag stops automatically.
                        _isDragging           = false;
                        GUIUtility.hotControl = 0;
                        Event.current.Use();
                    }
                    break;

                case EventType.MouseDrag:
                    if (_isDragging)
                    {
                        var ray   = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
                        var plane = new Plane(transform.up, _mouseDownMapPinPlanePositionInMapLocalSpace);
                        if (plane.Raycast(ray, out var enter))
                        {
                            Vector3 updatedHitPointInLocalSpace = mapRenderer.transform.worldToLocalMatrix * ray.GetPoint(enter);
                            var     newDeltaInLocalSpace        = updatedHitPointInLocalSpace - _mouseDownMapPinPlanePositionInMapLocalSpace;
                            var     newDeltaInMercator          = new MercatorCoordinate(newDeltaInLocalSpace.x, newDeltaInLocalSpace.z) / Math.Pow(2, mapRenderer.ZoomLevel - 1);
                            var     newLocation = (_mouseDownMapPinPositionInMercatorSpace + newDeltaInMercator).ToLatLon();

                            Undo.RecordObject(target, "Changed Location");
                            mapPin.Location = newLocation;
                        }
                        Event.current.Use();
                    }
                    break;

                case EventType.Repaint:
                    if (_isHovered || _isDragging)
                    {
                        // Change the cursor to make it obvious you can move the MapPin around.
                        EditorGUIUtility.AddCursorRect(new Rect(0, 0, 999999, 999999), MouseCursor.MoveArrow);
                    }

                    var planeY            = _isDragging ? (mapRenderer.transform.localToWorldMatrix * _mouseDownMapPinPlanePositionInMapLocalSpace).y : transform.position.y;
                    var rectanglePosition = new Vector3(transform.position.x, planeY, transform.position.z);
                    Handles.DrawSolidRectangleWithOutline(
                        new Vector3[]
                    {
                        rectanglePosition - size * mapRenderer.transform.right - size * mapRenderer.transform.forward,
                        rectanglePosition - size * mapRenderer.transform.right + size * mapRenderer.transform.forward,
                        rectanglePosition + size * mapRenderer.transform.right + size * mapRenderer.transform.forward,
                        rectanglePosition + size * mapRenderer.transform.right - size * mapRenderer.transform.forward
                    },
                        new Color(0.4f, 0.4f, 0.4f, 0.4f),
                        new Color(0, 0, 0, 0));

                    if (rectanglePosition != transform.position)
                    {
                        Handles.color = rectanglePosition.y > transform.position.y ? Color.red : Color.green;
                        Handles.DrawLine(transform.position, rectanglePosition);
                    }

                    Handles.color = _isHovered ? Color.yellow : Color.green;
                    Handles.color = _isDragging ? Color.white : Handles.color;
                    Handles.RectangleHandleCap(
                        controlId,
                        rectanglePosition,
                        transform.rotation * Quaternion.LookRotation(Vector3.up),
                        size,
                        EventType.Repaint);
                    break;
                }
            }
            else
            {
                Tools.hidden = false;
            }
        }
コード例 #18
0
        private void Update()
        {
            var touchCount = Input.touchCount;

            if (touchCount == 0)
            {
                if (_isInteracting)
                {
                    _isInteracting = false;
                    MapInteractionController.OnInteractionEnded?.Invoke();
                }

                _initialTouchPointDelta = 0.0f;
                _lastTouchCount         = touchCount;

                return;
            }

            var touch0          = Input.GetTouch(0);
            var touchPoint      = touch0.position;
            var touchPointDelta = 0.0f; // Used when there are two touch points (for pinch/zoom).

            // A single touch point is a pan, a double-tap, or a tap-and-hold.
            if (touchCount == 1)
            {
                // Disable zoom with two touch points.
                _initialTouchPointDelta = 0.0f;

                // Check for a double tap.
                if (touch0.phase == TouchPhase.Ended && touch0.tapCount > 1)
                {
                    // Do a double tap and early out.
                    var ray = Camera.ScreenPointToRay(touchPoint);
                    if (MapRenderer.Raycast(ray, out var hitInfo))
                    {
                        MapInteractionController.DoubleTapZoom(hitInfo.Location, 1.0f);
                    }

                    _tapAndHoldBeginTime = float.MaxValue; // Reset tap and hold.
                    _lastTouchCount      = 0;              // Reset interactions.

                    return;
                }

                // Check for tap and hold.
                if (_isInteracting)
                {
                    // If we're in the middle of an interaction (in this case, a pan), tap and hold doesn't apply.
                    if (_tapAndHoldBeginTime != float.MaxValue)
                    {
                        _tapAndHoldBeginTime = float.MaxValue;
                    }
                }
                else
                {
                    // Track tap and hold.
                    if (touch0.phase == TouchPhase.Began)
                    {
                        _tapAndHoldBeginTime = Time.time;
                    }
                    else if (!HasInitialPointMovedPastThreshold(touchPoint))
                    {
                        // The touch point has not moved enough to start an interaction, so we are in a stationary tap.
                        // Fire off the TapAndHold event once we exceed the hold threshold.
                        if ((Time.time - _tapAndHoldBeginTime) >= TapAndHoldThresholdInSeconds)
                        {
                            var ray = Camera.ScreenPointToRay(touchPoint);
                            if (MapRenderer.Raycast(ray, out var hitInfo))
                            {
                                MapInteractionController.OnTapAndHold?.Invoke(hitInfo.Location);

                                // Reset so we don't continually fire off TapAndHold events on subsequent frames where touch point continues
                                // to be stationary.
                                _tapAndHoldBeginTime = float.MaxValue;
                            }
                        }
                    }

                    // Reset tap and hold state if this touch has been ended or cancelled.
                    if (touch0.phase == TouchPhase.Canceled || touch0.phase == TouchPhase.Ended)
                    {
                        _tapAndHoldBeginTime = float.MaxValue;
                    }
                }
            }
            else // Touch count > 1, start tracking pinch.
            {
                // Touch delta can be calculated from first and second points.
                touchPointDelta = (Input.GetTouch(1).position - touch0.position).magnitude;

                // Touch point will be average between first and second.
                touchPoint += Input.GetTouch(1).position;
                touchPoint *= 0.5f;

                _tapAndHoldBeginTime = float.MinValue; // Reset tap and hold.
            }

            // In case a second touch point is added or removed, or more generally, if this is the first frame of any touch interaction,
            // we'll need to reset the target coordiante... This involves raycasting the map to see where the touch initally hits.
            var resetInteractionTarget = _lastTouchCount != touchCount;

            if (resetInteractionTarget)
            {
                var ray = Camera.ScreenPointToRay(touchPoint);
                if (MapRenderer.Raycast(ray, out var hitInfo))
                {
                    // We have a hit, so set up some initial variables.
                    _interactionTargetCoordinate = hitInfo.Location.LatLon.ToMercatorCoordinate();
                    _interactionTargetAltitude   = hitInfo.Location.AltitudeInMeters;
                    _initialTouchPointDelta      = 0.0f;
                    _initialTouchPoint           = touchPoint;
                    _lastTouchCount = touchCount;
                }
                else
                {
                    // The touch point didn't hit the map, so end any active interactions.
                    if (_isInteracting)
                    {
                        _lastTouchCount = 0;
                        _isInteracting  = false;
                        MapInteractionController.OnInteractionEnded?.Invoke();
                    }
                }
            }
            else
            {
                // If we've made it to this case, the touch point has hit the map and we're either waiting for a movement
                // to occur which exceeds the touch interaction thresholds, or a movement is actively happening and we
                // need to update the map's state (i.e., the center and zoom properties).

                var touchPointDeltaToInitialDeltaRatio = touchPointDelta / _initialTouchPointDelta;

                if (!_isInteracting)
                {
                    // Check if the initial interaction touch points have moved past a threshold to consider this a pan or zoom.
                    if (touchPointDeltaToInitialDeltaRatio > 1.05 || HasInitialPointMovedPastThreshold(touchPoint))
                    {
                        _isInteracting = true;
                        MapInteractionController.OnInteractionStarted?.Invoke();
                    }
                }

                if (_isInteracting)
                {
                    // We're inside an intersection, handle pinch/zoom first.
                    if (touchPointDelta > 0.0f)
                    {
                        var isInitialZoomFrame = _initialTouchPointDelta == 0.0;
                        if (isInitialZoomFrame)
                        {
                            _initialTouchPointDelta        = touchPointDelta;
                            _initialMapDimensionInMercator = Mathf.Pow(2, MapRenderer.ZoomLevel - 1);
                        }
                        else
                        {
                            var newMapDimensionInMercator = touchPointDeltaToInitialDeltaRatio * _initialMapDimensionInMercator;
                            var newZoomLevel = Math.Log(newMapDimensionInMercator) / Math.Log(2) + 1;
                            MapRenderer.ZoomLevel = Mathf.Clamp((float)newZoomLevel, MapRenderer.MinimumZoomLevel, MapRenderer.MaximumZoomLevel);
                        }
                    }

                    // Handle panning last. Zoom is handled above in the pinch case, and it should be updated prior to panning.
                    {
                        var ray = Camera.ScreenPointToRay(touchPoint);
                        MapInteractionController.PanAndZoom(ray, _interactionTargetCoordinate, _interactionTargetAltitude, 0.0f);
                    }
                }
            }
        }
コード例 #19
0
    private void Update()
    {
        var isInteractingWithMap = _panningPointer != null;

        if (isInteractingWithMap &&
            CoreServices.InputSystem.FocusProvider.TryGetFocusDetails(_panningPointer, out FocusDetails focusDetails))
        {
            // Determine amount to zoom in or out.
            var directionalZoomAmount = 0.0f;
            var zoomMagnitude         = _currentZoomValue.magnitude;
            if (zoomMagnitude > JoystickDeadZone)
            {
                var angle = Mathf.Rad2Deg * Mathf.Abs(Mathf.Atan2(_currentZoomValue.y, _currentZoomValue.x));
                if (Mathf.Abs(90.0f - angle) < 75)
                {
                    directionalZoomAmount = _currentZoomValue.y;
                }
            }

            // Compute more terms if there is any zoom to apply.
            var zoomRatio = 1.0;
            var zoomLevelToUseForInteraction = _mapRenderer.ZoomLevel;
            if (directionalZoomAmount != 0)
            {
                var zoomSpeed    = Mathf.Lerp(0.01f, 0.045f, _zoomSpeed);
                var zoomToApply  = zoomSpeed * directionalZoomAmount;
                var oldZoomLevel = _mapRenderer.ZoomLevel;
                var newZoomLevel =
                    Math.Min(
                        _mapRenderer.MaximumZoomLevel,
                        Math.Max(_mapRenderer.MinimumZoomLevel, oldZoomLevel + zoomToApply));
                zoomRatio = Math.Pow(2, oldZoomLevel - 1) / Math.Pow(2, newZoomLevel - 1);
                zoomLevelToUseForInteraction = newZoomLevel;
            }

            // The _startingPointInLocalSpace can be updated now as zoom changed so it's altitude may change as well.
            // A future improvement to make here is to actually requery the altitude of the _startingPointInMercatorSpace,
            // as this altitude can also change based on the level of detail being shown.
            var offsetAltitudeInMeters = _startingAltitudeInMeters - _mapRenderer.ElevationBaseline;
            var equatorialCircumferenceInLocalSpace = Math.Pow(2, zoomLevelToUseForInteraction - 1);
            var altitudeInLocalSpace =
                offsetAltitudeInMeters *
                _startingMercatorScale *
                (equatorialCircumferenceInLocalSpace / MapRendererTransformExtensions.EquatorialCircumferenceInWgs84Meters);
            _startingPointInLocalSpace.y = (float)(_mapRenderer.LocalMapBaseHeight + altitudeInLocalSpace);

            // Now we can raycast an imaginary plane orignating from the updated _startingPointInLocalSpace.
            var rayPositionInMapLocalSpace  = _mapRenderer.transform.InverseTransformPoint(_panningPointer.Position);
            var rayDirectionInMapLocalSpace = _mapRenderer.transform.InverseTransformDirection(_panningPointer.Rotation * Vector3.forward).normalized;
            var rayInMapLocalSpace          = new Ray(rayPositionInMapLocalSpace, rayDirectionInMapLocalSpace.normalized);
            var hitPlaneInMapLocalSpace     = new Plane(Vector3.up, _startingPointInLocalSpace);
            if (hitPlaneInMapLocalSpace.Raycast(rayInMapLocalSpace, out float enter))
            {
                // This point will be used to determine how much to translate the map.
                // Decaying the resulting position applies some smoothing to the input.
                var panSmoothness = Mathf.Lerp(0.0f, 0.5f, _panSmoothness);
                _currentPointInLocalSpace =
                    DynamicExpDecay(_currentPointInLocalSpace, rayInMapLocalSpace.GetPoint(enter), panSmoothness);

                // Also override the FocusDetails so that the pointer ray tracks with the map.
                // Otherwise, it would remain fixed in world space.
                focusDetails.Point           = _mapRenderer.transform.TransformPoint(_currentPointInLocalSpace);
                focusDetails.PointLocalSpace = _currentPointInLocalSpace;
                CoreServices.InputSystem.FocusProvider.TryOverrideFocusDetails(_panningPointer, focusDetails);
            }

            // Apply zoom now, if needed.
            if (directionalZoomAmount != 0)
            {
                _mapRenderer.ZoomLevel = zoomLevelToUseForInteraction;
                var deltaToCenterInMercatorSpace         = _startingPointInMercator - _startingMapCenterInMercator;
                var adjustedDeltaToCenterInMercatorSpace = zoomRatio * deltaToCenterInMercatorSpace;
                _startingMapCenterInMercator = _startingPointInMercator - adjustedDeltaToCenterInMercatorSpace;
            }

            // Apply pan translation.
            var deltaInLocalSpace          = _currentPointInLocalSpace - _startingPointInLocalSpace;
            var deltaInMercatorSpace       = MapRendererTransformExtensions.TransformLocalDirectionToMercator(deltaInLocalSpace, zoomLevelToUseForInteraction);
            var newCenterInMercator        = _startingMapCenterInMercator - deltaInMercatorSpace;
            var newClampedCenterInMercator = new MercatorCoordinate(newCenterInMercator.X, Math.Max(Math.Min(0.5, newCenterInMercator.Y), -0.5));

            _mapRenderer.Center = newClampedCenterInMercator.ToLatLon();
        }
    }
コード例 #20
0
 public void ResolutionSmallZ() => Assert.Throws <ArgumentOutOfRangeException>(() =>
 {
     double res = MercatorCoordinate.Resolution(-1, Tile.DefaultSize);
 });
コード例 #21
0
 public void CreateMercatorCoordinateBigLon() => Assert.Throws <ArgumentOutOfRangeException>(() =>
 {
     MercatorCoordinate coord = new MercatorCoordinate(20046377.0, Locations.TokyoMercatorLatitude);
 });
コード例 #22
0
 public void CreateMercatorCoordinateBigLat() => Assert.Throws <ArgumentOutOfRangeException>(() =>
 {
     MercatorCoordinate coord = new MercatorCoordinate(Locations.TokyoMercatorLongitude, 20048967.0);
 });