ThinkGeo.com    |     Documentation    |     Premium Support

WrappingMode.WrapDateLine, cannot drag objects unless the map is centered

I have the following map, with WrappingMode.WrapDateTime on the overlays.

If I pan the map so that the Pacific Ocean is centered. I can select objects on the map, and they are highlighted, but I cannot click and drag any object that is not between the -180 to 180 longitudes. In order to select those, I had to adjust the cursor position to select the objects from the map’s original extent. So, in the example below the objects on the eastern coast of Asia cannot be moved, unless I pan the map back to the east and the objects on the west coast coast of the US cannot be moved unless I pan back to it. (FYI the added redline is the date line.)

Is there a solution to allow dragging of objects displayed on the parts of the map that is not technically in the original extent?

Hi James,

Can you share the code how you select / drag an object on the map, are you using EditOverlay or some other way?

Thanks,
Ben

Basically this:
public class MultiSelectEditInteractiveOverlay : EditInteractiveOverlay
{
private PointStyle controlPointStyle;
private PointStyle selectedControlPointStyle;

private PointShape startPoint;
Collection<int> ids;

public MultiSelectEditInteractiveOverlay()
    : base()
{
    this.CanAddVertex = false;
    this.CanDrag = true;
    this.CanRemoveVertex = false;
    this.CanResize = false;
    this.CanRotate = false;

    ids = new Collection<int>();
}

public PointStyle ControlPointStyle
{
    get { return controlPointStyle; }
    set { controlPointStyle = value; }
}

public PointStyle SelectedControlPointStyle
{
    get { return selectedControlPointStyle; }
    set { selectedControlPointStyle = value; }
}

/// <summary>
/// When drag and drop is complete add the shapes to the new location. 
/// </summary>
/// <param name="targetPointShape"></param>
protected override void EndEditingCore(PointShape targetPointShape)
{
    ClearAllControlPoints();
    CalculateCustomAllControlPoints();
    ShowAllControlPointLayers(true);
}

/// <summary>
/// Override the mouse enter core from the base.
/// </summary>
/// <param name="interactionArguments"></param>
/// <returns></returns>
protected override InteractiveResult MouseEnterCore(InteractionArguments interactionArguments)
{
    var result = base.MouseEnterCore(interactionArguments);
    return base.MouseEnterCore(interactionArguments);
}
/// <summary>
/// Process the mouse down click and get all selected features and their starting locations.
/// </summary>
/// <param name="interactionArguments"></param>
/// <returns></returns>
protected override InteractiveResult MouseDownCore(InteractionArguments interactionArguments)
{
    InteractiveResult result = base.MouseDownCore(interactionArguments);
    startPoint = null;
    if (interactionArguments.MouseButton == MapMouseButton.Left)
    {
       //If there are features selected calculate the starting points. 
        if (EditShapesLayer.InternalFeatures.Count > 0)
        {
            //Search are is the entire map for calculating the starting feature location.
            RectangleShape searchingArea = new RectangleShape(interactionArguments.WorldX - interactionArguments.SearchingTolerance, interactionArguments.WorldY + interactionArguments.SearchingTolerance, interactionArguments.WorldX + interactionArguments.SearchingTolerance, interactionArguments.WorldY - interactionArguments.SearchingTolerance);

            //set the starting points
            foreach (Feature feature in DragControlPointsLayer.InternalFeatures)
            {
                BaseShape shape = feature.GetShape();
                if (shape.IsWithin(searchingArea))
                {
                    startPoint = new PointShape(interactionArguments.WorldX, interactionArguments.WorldY);
                }
            }

            //Define the start point used to calculate the location change point.  
            if (SetSelectedControlPoint(new PointShape(interactionArguments.WorldX, interactionArguments.WorldY), interactionArguments.SearchingTolerance))
            {
                startPoint = new PointShape(interactionArguments.WorldX, interactionArguments.WorldY);
            }


            //If no shape defined process base method.
            if (startPoint == null)
            {
                result = base.MouseDownCore(interactionArguments);
            }
        }
    }
    return result;
}

/// <summary>
/// Process the mouse move and collect the new location of all features being moved. 
/// </summary>
/// <param name="interactionArguments"></param>
/// <returns></returns>
protected override InteractiveResult MouseMoveCore(InteractionArguments interactionArguments)
{
    InteractiveResult result = new InteractiveResult();

    if (ControlPointType != ControlPointType.None)
    {
        //If dragging calculate the new feature locations.
        if (ControlPointType == ControlPointType.Drag)
        {
            //If no starting point return the default. 
            if (startPoint == null)
            {
                return result;
            }
            else
            {
                //Get the current mouse location.
                PointShape currentPoint = new PointShape(interactionArguments.WorldX, interactionArguments.WorldY);
                double xOffset = currentPoint.X - startPoint.X;
                double yOffset = currentPoint.Y - startPoint.Y;

                //Create a dictionary to hold the new location of the feature by id. 
                Dictionary<int, Feature> features = new Dictionary<int, Feature>();

                int index = 0;
                if (ids.Count == 0)
                {
                    //For each selected feature calculate the new map location using the mouse location in the DragControlPointsLayer.
                    //Add the new id and new location of the feature to the dictionary.
                    //This code may create duplicates in the dictionary and it is allow and handled later. 
                    foreach (Feature feature in EditShapesLayer.InternalFeatures)
                    {
                        foreach (Feature controlFeature in DragControlPointsLayer.InternalFeatures)
                        {
                            PointShape oldPoint = controlFeature.GetShape() as PointShape;
                                PointShape targetPoint = new PointShape(oldPoint.X + xOffset, oldPoint.Y + yOffset);
                                features.Add(index, DragFeature(feature, oldPoint, targetPoint));
                                index++;
                           
                        }
                    }
                }

                //Process the new location dictionary.
                foreach (var feature in features)
                {
                    //If the EditShapesLayer contains the feature set the location else add a new one.
                    //This handles possible duplicates created in the previous step. 
                    if (EditShapesLayer.InternalFeatures.Contains(feature.Value.Id))
                    {
                        EditShapesLayer.InternalFeatures[feature.Value.Id] = feature.Value;
                    }
                    else
                    {
                        EditShapesLayer.InternalFeatures.Add(feature.Value.Id, feature.Value);
                    }
                }
                //Clear and set the new locations.
                CalculateCustomAllControlPoints();

                //Set the start location equal to the current. 
                startPoint = currentPoint.CloneDeep() as PointShape;
                //Redraw the features in the new location.
                result.InteractiveOverlayUpdateMode = InteractiveOverlayUpdateMode.Update;
                result.ProcessOtherOverlaysMode = ProcessOtherOverlaysMode.DoNotProcessOtherOverlays;
            }
        }
        else
        {
            result = base.MouseMoveCore(interactionArguments);
        }
    }

    return result;
}

/// <summary>
/// Process the mouse click and set the final feature locations.
/// </summary>
/// <param name="interactionArguments"></param>
/// <returns></returns>
protected override InteractiveResult MouseClickCore(InteractionArguments interactionArguments)
{
    InteractiveResult result = base.MouseClickCore(interactionArguments);
    //if not InteractiveOverlayUpdateMode.Update then set the final locations and redraw.
    if (result.InteractiveOverlayUpdateMode != InteractiveOverlayUpdateMode.Update)
    {
        //Set the search area as the entire map. 
        RectangleShape searchingArea = new RectangleShape(interactionArguments.WorldX - interactionArguments.SearchingTolerance, interactionArguments.WorldY + interactionArguments.SearchingTolerance, interactionArguments.WorldX + interactionArguments.SearchingTolerance, interactionArguments.WorldY - interactionArguments.SearchingTolerance);

        //Get the current feature locations. 
        foreach (Feature feature in EditShapesLayer.InternalFeatures)
        {
            BaseShape shape = feature.GetShape();
            //If the feature is on the map and nothing is still being moved set the location and redraw.
            if (shape.Intersects(searchingArea) && DragControlPointsLayer.InternalFeatures.Count == 0)
            {
                //Clear and set the final location.
                CalculateCustomAllControlPoints();
                //Redraw all features. 
                result.InteractiveOverlayUpdateMode = InteractiveOverlayUpdateMode.Update;
                result.ProcessOtherOverlaysMode = ProcessOtherOverlaysMode.DoNotProcessOtherOverlays;
            }
        }
    }
    return result;
}

/// <summary>
/// Set the control points for the map features.
/// </summary>
public void CalculateCustomAllControlPoints()
{
    //Clear the current control points.
    ClearAllControlPoints();

    //Set the controls points by property. 
    if (CanDrag) { CalculateCustomDragControlPoint(); }
    if (CanRotate) { CalculateRotateControlPoints(); }
    if (CanResize) { CalculateResizeControlPoints(); }
    if (CanReshape) { CalculateVertexControlPoints(); }

    //Draw and show the features in the new location. 
    ShowAllControlPointLayers(true);

}

/// <summary>
/// Make all the control point layers visible.
/// </summary>
/// <param name="visiable"></param>
private void ShowAllControlPointLayers(bool visiable)
{
    DragControlPointsLayer.IsVisible = visiable;
    RotateControlPointsLayer.IsVisible = visiable;
    ResizeControlPointsLayer.IsVisible = visiable;
    ExistingControlPointsLayer.IsVisible = visiable;
}

/// <summary>
/// Set the current location of the features. 
/// </summary>
protected void CalculateCustomDragControlPoint()
{
    LineShape centerPointsLine = new LineShape();

    //For each selected feature, set the current location. 
    foreach (Feature feature in EditShapesLayer.InternalFeatures)
    {
        //Calculate the new location of the feature.
        IEnumerable<Feature> dragControlPoints = CalculateDragControlPointsCore(feature);
        BaseShape baseShape = feature.GetShape();
        PointShape centerPointShape = baseShape.GetCenterPoint();

        //If the center point of the features is not set, set it based on the feature's bounding box. 
        if (double.IsNaN(centerPointShape.X) || double.IsNaN(centerPointShape.Y) || double.IsInfinity(centerPointShape.X) || double.IsInfinity(centerPointShape.Y))
        {
            centerPointShape = baseShape.GetBoundingBox().UpperLeftPoint;
        }

        //Add the feature center point to the list of points.
        centerPointsLine.Vertices.Add(new Vertex(centerPointShape));
    }

    //Set the first feature's center point. 
    if (centerPointsLine != null && centerPointsLine.Vertices.Count > 0)
    {
        PointShape centerPointInCenterPoints = new PointShape(centerPointsLine.Vertices[0]);

        if (centerPointsLine.Vertices.Count == 2)
        {
            centerPointInCenterPoints = centerPointsLine.GetCenterPoint();
        }
        else if (centerPointsLine.Vertices.Count > 2)
        {
            centerPointInCenterPoints = centerPointsLine.GetBoundingBox().GetCenterPoint();
        }

        //Add the first feature's center point as the control point.
        Feature addedFeature = new Feature(centerPointInCenterPoints);
        DragControlPointsLayer.InternalFeatures.Add(addedFeature);
    }
}

}

Ben,

I think the issue is there is no internal feature at that location. How are the images rendered when panning? The images displayed are for features stored from -180 to 180 longitudes. When panning how are those images rendered across the additional map tiles. Can those be accessed and related back to the internal feature at the original location?

For example:
EMEDS +10.3 is an internal feature at Latitude 58.11163 and Longitude 130.07813. Panning to the West, the longitude of the image on the Map becomes -229.92187, when the dateline is centered in the Pacific Ocean. In order to highlight that image as shown, I had to select the actual feature at 130.07813.

Thanks,
James

Hi James,

Here we created a sample for you. In which you can draw a point, edit it and move it outside the “center world”,

Then hit “Navigation Mode” to save it and the point will show up on each world.

The key code locates in the method UpdateFeatureWithinStandardExtent, in which we always save the updated feature within the “center map” extent. Another thing is to set the pointLayer to WrapDateline mode using the following code:

   layerOverlay.WrappingMode = WrappingMode.WrapDateline;
   layerOverlay.WrappingExtent = MaxExtents.OsmMaps;

The demo is using OpenStreetMap, you might need to change it to DecimalDegrees accordingly to match your background. Let us know if you have any questions.

WrapDateLine_10455_06192024.zip (8.6 KB)

Thanks,
Ben

Ben,

Can I drag it after it is placed from the point outside the center? That is my issue. I have no problems creating things outside the center. It is dragging them to a new location when I am not in the center after I do.

Also, since we are using shape files have the map set to:
overlay.WrappingMode = WrappingMode.WrapDateline;
overlay.WrappingExtent = MaxExtents.DecimalDegree ;

Is MaxExtents.OsmMaps better somehow?

Thanks,
James

Hi James,

When adding features to the EditShapeOverlay to start editing, you can translate the features to the “current world”. Similar to LocateFeatureWithinStandardExtent() in the sample which translate a feature to the center world, you need to have a similar method doing the other way to translate a feature from the center world to the current one before adding it to the EditShapeOverlay.

If you are using DecimalDegrees, overlay.WrappingExtent = MaxExtents.DecimalDegree ; is correct.

Thanks,
Ben

Ben,

So, its needs to go into the edit shape overlay at the position I click on it at. I should have realized that,

Thanks,
James

Sure, just let us know if you have more questions.