From 95ee8e71b3912d8f29465726c8493a98a70de189 Mon Sep 17 00:00:00 2001 From: Mathijs Dumon Date: Fri, 14 Dec 2018 11:43:17 +0100 Subject: [PATCH 1/3] Added snapping modes: to segments, to vertices or both --- src/COM classes/ShapeEditor.cpp | 16 ++++++ src/COM classes/ShapeEditor.h | 4 ++ src/COM classes/Shapefile.cpp | 35 +++++++++++++ src/COM classes/Shapefile.h | 1 + src/ComHelpers/ShapefileHelper.cpp | 80 +++++++++++++++++++++++++++++- src/ComHelpers/ShapefileHelper.h | 2 + src/Control/Map_Edit.cpp | 38 ++++++++------ src/Control/Map_Identifier.cpp | 48 ++++++++++-------- src/Editor/ActiveShape.cpp | 5 ++ src/Editor/ActiveShape.h | 7 +++ src/Editor/EditorBase.cpp | 12 +++++ src/Editor/EditorBase.h | 2 + src/Editor/EditorHelper.cpp | 11 ++++ src/Editor/EditorHelper.h | 1 + src/MapWinGIS.idl | 16 ++++++ 15 files changed, 240 insertions(+), 38 deletions(-) diff --git a/src/COM classes/ShapeEditor.cpp b/src/COM classes/ShapeEditor.cpp index de7300ad..cd0d6f13 100644 --- a/src/COM classes/ShapeEditor.cpp +++ b/src/COM classes/ShapeEditor.cpp @@ -782,6 +782,22 @@ STDMETHODIMP CShapeEditor::put_SnapBehavior(tkLayerSelection newVal) return S_OK; } +// ******************************************************* +// get_SnapMode() +// ******************************************************* +STDMETHODIMP CShapeEditor::get_SnapMode(tkSnapMode* pVal) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *pVal = _snapMode; + return S_OK; +} +STDMETHODIMP CShapeEditor::put_SnapMode(tkSnapMode newVal) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + _snapMode = newVal; + return S_OK; +} + // ******************************************************* // EditorState // ******************************************************* diff --git a/src/COM classes/ShapeEditor.h b/src/COM classes/ShapeEditor.h index f1c1e480..d69968bd 100644 --- a/src/COM classes/ShapeEditor.h +++ b/src/COM classes/ShapeEditor.h @@ -27,6 +27,7 @@ class ATL_NO_VTABLE CShapeEditor : _highlightShapes = lsAllLayers; _snapTolerance = 10; _snapBehavior = lsAllLayers; + _snapMode = smVerticesAndLines; _state = esNone; _mapCallback = NULL; _isSubjectShape = false; @@ -113,6 +114,8 @@ class ATL_NO_VTABLE CShapeEditor : STDMETHOD(put_HighlightVertices)(tkLayerSelection newVal); STDMETHOD(get_SnapBehavior)(tkLayerSelection* pVal); STDMETHOD(put_SnapBehavior)(tkLayerSelection newVal); + STDMETHOD(get_SnapMode)(tkSnapMode* pVal); + STDMETHOD(put_SnapMode)(tkSnapMode newVal); STDMETHOD(get_EditorState)(tkEditorState* pVal); STDMETHOD(put_EditorState)(tkEditorState newVal); STDMETHOD(StartEdit)(LONG LayerHandle, LONG ShapeIndex, VARIANT_BOOL* retVal); @@ -154,6 +157,7 @@ class ATL_NO_VTABLE CShapeEditor : ICallback * _globalCallback; VARIANT_BOOL _visible; tkLayerSelection _highlightShapes; + tkSnapMode _snapMode; double _snapTolerance; tkLayerSelection _snapBehavior; EditorBase* _activeShape; diff --git a/src/COM classes/Shapefile.cpp b/src/COM classes/Shapefile.cpp index 5a0969a3..84b6a65a 100644 --- a/src/COM classes/Shapefile.cpp +++ b/src/COM classes/Shapefile.cpp @@ -3174,6 +3174,41 @@ STDMETHODIMP CShapefile::GetClosestVertex(double x, double y, double maxDistance return S_OK; } +// ***************************************************************** +// GetClosestSnapPosition() +// ***************************************************************** +STDMETHODIMP CShapefile::GetClosestSnapPosition(double x, double y, double maxDistance, + long* shapeIndex, double* fx, double* fy, double* distance, VARIANT_BOOL* retVal) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + *retVal = VARIANT_FALSE; + *shapeIndex = -1; + + bool result = false; + if (maxDistance <= 0.0) + { + // search through all shapefile + std::vector ids; + for (size_t i = 0; i < _shapeData.size(); i++) + { + ids.push_back(i); + } + result = ShapefileHelper::GetClosestSnapPosition(this, x, y, maxDistance, ids, shapeIndex, *fx, *fy, *distance); + } + else + { + std::vector ids; + Extent box(x - maxDistance, x + maxDistance, y - maxDistance, y + maxDistance); + if (this->SelectShapesCore(box, 0.0, SelectMode::INTERSECTION, ids, false)) + { + result = ShapefileHelper::GetClosestSnapPosition(this, x, y, maxDistance, ids, shapeIndex, *fx, *fy, *distance); + } + } + *retVal = result ? VARIANT_TRUE : VARIANT_FALSE; + return S_OK; +} + // ***************************************************************** // HasInvalidShapes() // ***************************************************************** diff --git a/src/COM classes/Shapefile.h b/src/COM classes/Shapefile.h index f57f474f..76af0f21 100644 --- a/src/COM classes/Shapefile.h +++ b/src/COM classes/Shapefile.h @@ -213,6 +213,7 @@ class ATL_NO_VTABLE CShapefile : STDMETHOD(EditAddShape)(IShape* shape, long* shapeIndex); STDMETHOD(EditAddField)(BSTR name, FieldType type, int precision, int width, long* fieldIndex); STDMETHOD(GetClosestVertex)(double x, double y, double maxDistance, long* shapeIndex, long* pointIndex, double* distance, VARIANT_BOOL* retVal); + STDMETHOD(GetClosestSnapPosition)(double x, double y, double maxDistance, long* shapeIndex, double* fx, double* fy, double* distance, VARIANT_BOOL* retVal); STDMETHOD(get_ShapeCategory2)(long ShapeIndex, BSTR* categoryName); STDMETHOD(put_ShapeCategory2)(long ShapeIndex, BSTR categoryName); STDMETHOD(get_ShapeCategory3)(long ShapeIndex, IShapefileCategory** category); diff --git a/src/ComHelpers/ShapefileHelper.cpp b/src/ComHelpers/ShapefileHelper.cpp index cf59307c..84510062 100644 --- a/src/ComHelpers/ShapefileHelper.cpp +++ b/src/ComHelpers/ShapefileHelper.cpp @@ -382,10 +382,88 @@ bool ShapefileHelper::GetClosestPoint(IShapefile* sf, double x, double y, double shp->Release(); } } - // dist = minDist; + dist = minDist; return minDist < maxDistance; } +// ***************************************************************** +// GetClosestPoint() +// ***************************************************************** +bool ShapefileHelper::GetClosestSnapPosition(IShapefile* sf, double x, double y, double maxDistance, std::vector& ids, + long* shapeIndex, double& fx, double& fy, double& dist) +{ + if (!sf) return false; + + VARIANT_BOOL vb; + double minDist = DBL_MAX; + for (long id : ids) + { + VARIANT_BOOL visible; + sf->get_ShapeVisible(id, &visible); + if (!visible) continue; + + IShape* shp = nullptr; + sf->get_Shape(id, &shp); + if (shp) + { + long numPoints; + shp->get_NumPoints(&numPoints); + double xSeg, ySeg; + + for (long j = 0; j < numPoints; j++) + { + if (j + 1 >= numPoints) { + shp->get_XY(j, &xSeg, &ySeg, &vb); + } + else { + double xPnt1, yPnt1; + double xPnt2, yPnt2; + shp->get_XY(j, &xPnt1, &yPnt1, &vb); + shp->get_XY(j+1, &xPnt2, &yPnt2, &vb); + ShapefileHelper::GetClosestPointOnSegment(xPnt1, yPnt1, xPnt2, yPnt2, x, y, &xSeg, &ySeg); + } + const double distance = sqrt(pow(x - xSeg, 2.0) + pow(y - ySeg, 2.0)); + if (distance < minDist && distance < maxDistance) + { + minDist = distance; + *shapeIndex = id; + fx = xSeg; + fy = ySeg; + } + } + shp->Release(); + } + } + dist = minDist; + return minDist < maxDistance; +} + +// ***************************************************************** +// GetClosestPoint() +// ***************************************************************** +bool ShapefileHelper::GetClosestPointOnSegment(double ax, double ay, double bx, double by, double px, double py, + double *rx, double *ry) { + + double apx = px - ax; + double apy = py - ay; + double abx = bx - ax; + double aby = by - ay; + + double ab2 = abx * abx + aby * aby; + double ap_ab = apx * abx + apy * aby; + double t = ap_ab / ab2; + if (t < 0) { + t = 0; + } + else if (t > 1) { + t = 1; + } + *rx = ax + abx * t; + *ry = ay + aby * t; + + return true; +} + // ******************************************************************** // PointInPolygon() // ******************************************************************** diff --git a/src/ComHelpers/ShapefileHelper.h b/src/ComHelpers/ShapefileHelper.h index d2d4d6b0..18d0a028 100644 --- a/src/ComHelpers/ShapefileHelper.h +++ b/src/ComHelpers/ShapefileHelper.h @@ -20,6 +20,8 @@ class ShapefileHelper static IShapefile* CloneSelection(IShapefile* sf); static bool ShapeSelected(IShapefile* sf, int shapeIndex); static bool GetClosestPoint(IShapefile* sf, double x, double y, double maxDistance, std::vector& ids, long* shapeIndex, long* pointIndex, double& dist); + static bool GetClosestSnapPosition(IShapefile * sf, double x, double y, double maxDistance, std::vector& ids, long * shapeIndex, double& fx, double& fy, double & dist); + static bool GetClosestPointOnSegment(double ax, double ay, double bx, double by, double px, double py, double * rx, double * ry); static bool PointInPolygon(IShapefile* sf, long ShapeIndex, double x, double y); static bool BoundsWithinPolygon(IShapefile* sf, int shapeIndex, double b_minX, double b_minY, double b_maxX, double b_maxY); static bool ShapeTypeIsM(IShapefile* sf); diff --git a/src/Control/Map_Edit.cpp b/src/Control/Map_Edit.cpp index 52a7995f..943ee212 100644 --- a/src/Control/Map_Edit.cpp +++ b/src/Control/Map_Edit.cpp @@ -77,22 +77,28 @@ bool CMapView::SnappingIsOn(bool shift) // ************************************************************ bool CMapView::HandleOnMouseMoveShapeEditor(int x, int y, long nFlags) { + + tkLayerSelection behavior; + _shapeEditor->get_SnapBehavior(&behavior); + + double projX, projY; + bool snapped = FindSnapPointCore(x, y, &projX, &projY); + if (!snapped) { + PixelToProjection(x, y, projX, projY); + GetEditorBase()->ClearSnapPoint(); + } + else + GetEditorBase()->SetSnapPoint(projX, projY, true); + if ((_dragging.Operation == DragMoveVertex || _dragging.Operation == DragMoveShape || _dragging.Operation == DragMovePart)) // && (nFlags & MK_LBUTTON) && _leftButtonDown { _dragging.Snapped = false; - tkLayerSelection behavior; - _shapeEditor->get_SnapBehavior(&behavior); - if (behavior == lsAllLayers && _dragging.Operation == DragMoveVertex) - { - double xFound, yFound; - if (this->FindSnapPointCore(x, y, &xFound, &yFound)) { - double xNew, yNew; - ProjToPixel(xFound, yFound, &xNew, &yNew); - _dragging.SetSnapped(xFound, yFound); - } - } + if (behavior == lsAllLayers && _dragging.Operation == DragMoveVertex && snapped) + _dragging.SetSnapped(projX, projY); + else + GetEditorBase()->ClearSnapPoint(); _dragging.HasMoved = true; // in case of vertex moving underlying data is changed in the process (to update displayed length); @@ -122,12 +128,10 @@ bool CMapView::HandleOnMouseMoveShapeEditor(int x, int y, long nFlags) } else { - tkEditorBehavior behavior; - _shapeEditor->get_EditorBehavior(&behavior); + tkSnapMode snapMode; + _shapeEditor->get_SnapMode(&snapMode); EditorBase* base = GetEditorBase(); bool handled = false; - double projX, projY; - this->PixelToProjection(x, y, projX, projY); // highlighting of vertices if (behavior == ebVertexEditor) @@ -146,7 +150,7 @@ bool CMapView::HandleOnMouseMoveShapeEditor(int x, int y, long nFlags) } else { if (base->ClearHighlightedVertex()) { - _canUseMainBuffer = false; + _canUseMainBuffer = false; return true; } } @@ -155,6 +159,8 @@ bool CMapView::HandleOnMouseMoveShapeEditor(int x, int y, long nFlags) // highlighting parts if (behavior == ebPartEditor) //if (nFlags & MK_CONTROL) { { + base->ClearSnapPoint(); + if (base->ClearHighlightedVertex()) _canUseMainBuffer = false; diff --git a/src/Control/Map_Identifier.cpp b/src/Control/Map_Identifier.cpp index 5a95d910..ddb49124 100644 --- a/src/Control/Map_Identifier.cpp +++ b/src/Control/Map_Identifier.cpp @@ -39,18 +39,20 @@ VARIANT_BOOL CMapView::FindSnapPoint(double tolerance, double xScreen, double yS double distance; double minDist = DBL_MAX; - IShapefile* foundShapefile = NULL; long foundShapeIndex; long foundPointIndex; bool digitizing = EditorHelper::IsDigitizingCursor((tkCursorMode)m_cursorMode); tkLayerSelection behavior; _shapeEditor->get_SnapBehavior(&behavior); + tkSnapMode mode; + _shapeEditor->get_SnapMode(&mode); long currentHandle = -1; bool currentLayerOnly = behavior == lsActiveLayer && digitizing; if (currentLayerOnly) _shapeEditor->get_LayerHandle(¤tHandle); + bool result = false; for (long i = 0; i < this->GetNumLayers(); i++) { long layerHandle = this->GetLayerHandle(i); @@ -68,33 +70,37 @@ VARIANT_BOOL CMapView::FindSnapPoint(double tolerance, double xScreen, double yS sf->get_Snappable(&snappable); if (snappable) { - sf->GetClosestVertex(x, y, maxDist, &shapeIndex, &pointIndex, &distance, &vb); - if (vb) - { - if (distance < minDist) + // Try snapping to a vertex first: + if (mode == smVertices || mode == smVerticesAndLines) { + sf->GetClosestVertex(x, y, maxDist, &shapeIndex, &pointIndex, &distance, &vb); + if (vb && distance < minDist) + { + IShape* shape = NULL; + sf->get_Shape(foundShapeIndex, &shape); + if (shape) + { + minDist = distance; + shape->get_XY(foundPointIndex, xFound, yFound, &vb); + shape->Release(); + result = true; + } + } + } + // If not snapped to a vertex, maybe try to snap to a segment: + if (!result && (mode == smVerticesAndLines || mode == smLines)) { + double xF, yF; + sf->GetClosestSnapPosition(x, y, maxDist, &shapeIndex, &xF, &yF, &distance, &vb); + if (vb && distance < minDist) { minDist = distance; - foundShapefile = sf; - foundPointIndex = pointIndex; - foundShapeIndex = shapeIndex; + *xFound = xF; + *yFound = yF; + result = true; } } } } } - - bool result = false; - if (minDist != DBL_MAX && foundShapefile) - { - IShape* shape = NULL; - foundShapefile->get_Shape(foundShapeIndex, &shape); - if (shape) - { - shape->get_XY(foundPointIndex, xFound, yFound, &vb); - shape->Release(); - result = true; - } - } return result; } diff --git a/src/Editor/ActiveShape.cpp b/src/Editor/ActiveShape.cpp index 416341d0..2d18ed9b 100644 --- a/src/Editor/ActiveShape.cpp +++ b/src/Editor/ActiveShape.cpp @@ -202,6 +202,11 @@ void ActiveShape::DrawData( Gdiplus::Graphics* g, bool dynamicBuffer, DrawPolygonArea(g, polyData, polySize, dynamicBuffer); delete[] polyData; } + + // finally draw the snap point position if needed + if (_showSnapPoint) { + g->DrawRectangle(&_redPen, (int)(_snapPointX - 3.0f + 0.5f), (int)(_snapPointY - 3.0f + 0.5f), 6, 6); + } } // **************************************************************** diff --git a/src/Editor/ActiveShape.h b/src/Editor/ActiveShape.h index 75221a58..d5b8a75d 100644 --- a/src/Editor/ActiveShape.h +++ b/src/Editor/ActiveShape.h @@ -27,6 +27,10 @@ class ActiveShape: public GeoShape _highlightedPart = -1; _inputMode = simMeasuring; + _showSnapPoint = false; + _snapPointX = 0; + _snapPointY = 0; + AnglePrecision = 1; AngleFormat = afDegrees; AreaDisplayMode = admMetric; @@ -90,6 +94,9 @@ class ActiveShape: public GeoShape int _highlightedVertex; int _selectedPart; int _highlightedPart; + bool _showSnapPoint; + int _snapPointX; + int _snapPointY; public: diff --git a/src/Editor/EditorBase.cpp b/src/Editor/EditorBase.cpp index 4dd6161f..45ad87ee 100644 --- a/src/Editor/EditorBase.cpp +++ b/src/Editor/EditorBase.cpp @@ -84,6 +84,18 @@ bool EditorBase::ClearHighlightedVertex() return false; } +void EditorBase::SetSnapPoint(double x, double y, bool visible) +{ + _snapPointX = x; + _snapPointY = y; + _showSnapPoint = true; +} + +void EditorBase::ClearSnapPoint() +{ + _showSnapPoint = false; +} + // ************************************************ // ClearHighlightedPart // ************************************************ diff --git a/src/Editor/EditorBase.h b/src/Editor/EditorBase.h index 48ff4a83..0669ac70 100644 --- a/src/Editor/EditorBase.h +++ b/src/Editor/EditorBase.h @@ -39,6 +39,8 @@ class EditorBase: public ActiveShape bool SetSelectedVertex(int index); bool ClearHighlightedPart(); bool ClearHighlightedVertex(); + void SetSnapPoint(double x, double y, bool visible); + void ClearSnapPoint(); int SelectPart(double xProj, double yProj); int GetClosestVertex(double projX, double projY, double tolerance); bool HasClosedPolygon(); diff --git a/src/Editor/EditorHelper.cpp b/src/Editor/EditorHelper.cpp index a7b6c31b..5560f119 100644 --- a/src/Editor/EditorHelper.cpp +++ b/src/Editor/EditorHelper.cpp @@ -133,4 +133,15 @@ tkLayerSelection EditorHelper::GetSnappingBehavior(IShapeEditor* editor) tkLayerSelection behavior; editor->get_SnapBehavior(&behavior); return behavior; +} + +// ******************************************************* +// GetSnappingBehavior() +// ******************************************************* +tkSnapMode EditorHelper::GetSnappingMode(IShapeEditor* editor) +{ + if (!editor) return smVerticesAndLines; + tkSnapMode mode; + editor->get_SnapMode(&mode); + return mode; } \ No newline at end of file diff --git a/src/Editor/EditorHelper.h b/src/Editor/EditorHelper.h index e7592e19..2270c4bf 100644 --- a/src/Editor/EditorHelper.h +++ b/src/Editor/EditorHelper.h @@ -11,6 +11,7 @@ class EditorHelper static void CopyOptionsFrom(IShapeEditor* editor, IShapeDrawingOptions* options); static bool OnCursorChanged(CShapeEditor* editor, bool clearEditor, tkCursorMode newCursor, bool& redrawNeeded); static tkLayerSelection GetSnappingBehavior(IShapeEditor* editor); + static tkSnapMode GetSnappingMode(IShapeEditor * editor); static bool IsSnappableCursor(tkCursorMode mode); }; diff --git a/src/MapWinGIS.idl b/src/MapWinGIS.idl index 422635e0..66583701 100644 --- a/src/MapWinGIS.idl +++ b/src/MapWinGIS.idl @@ -393,6 +393,18 @@ enum tkLayerSelection { lsActiveLayer = 2, }tkLayerSelection; +/************************** tkSnapMode Enumeration **********************/ +typedef +[ + uuid(AC54A94F-072E-4D91-BD22-E985BF2F957F), + helpstring("tkSnapMode"), +] +enum tkSnapMode { + smVertices = 0, + smVerticesAndLines = 1, + smLines = 2 +}tkSnapMode; + /************************** tkUndoOperation Enumeration **********************/ typedef [ @@ -3655,6 +3667,8 @@ enum tkScalebarUnits [propput, id(59)] HRESULT ShowLength([in] VARIANT_BOOL newVal); [id(60)] HRESULT Serialize([out, retval] BSTR* retVal); [id(61)] HRESULT Deserialize([in] BSTR state, [out, retval] VARIANT_BOOL* retVal); + [propget, id(63)] HRESULT SnapMode([out, retval] tkSnapMode* pVal); + [propput, id(63)] HRESULT SnapMode([in] tkSnapMode newVal); }; @@ -4170,6 +4184,8 @@ interface IShapefile : IDispatch{ [propget, id(142)] HRESULT IsGeographicProjection([out, retval] VARIANT_BOOL* retVal); [propget, id(143)] HRESULT Selectable([out, retval] VARIANT_BOOL* retVal); [propput, id(143)] HRESULT Selectable([in] VARIANT_BOOL newVal); + [id(200), helpstring("method GetClosestSnapPosition")] HRESULT GetClosestSnapPosition([in]double x, [in]double y, [in]double maxDistance, + [out]long* shapeIndex, [out] double* fx, [out] double* fy, [out] double* distance, [out, retval]VARIANT_BOOL* retVal); }; /**************************** Shape Interface ***********************/ From 1f95f01230750899ff6be01338956d524d8cd3be Mon Sep 17 00:00:00 2001 From: Mathijs Dumon Date: Fri, 14 Dec 2018 10:52:44 +0100 Subject: [PATCH 2/3] Bugfixes to line snapping support --- src/Control/Map_Edit.cpp | 3 --- src/Control/Map_Events.cpp | 3 +++ src/Control/Map_Identifier.cpp | 23 +++++++++++++---------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/Control/Map_Edit.cpp b/src/Control/Map_Edit.cpp index 943ee212..049bec53 100644 --- a/src/Control/Map_Edit.cpp +++ b/src/Control/Map_Edit.cpp @@ -128,8 +128,6 @@ bool CMapView::HandleOnMouseMoveShapeEditor(int x, int y, long nFlags) } else { - tkSnapMode snapMode; - _shapeEditor->get_SnapMode(&snapMode); EditorBase* base = GetEditorBase(); bool handled = false; @@ -159,7 +157,6 @@ bool CMapView::HandleOnMouseMoveShapeEditor(int x, int y, long nFlags) // highlighting parts if (behavior == ebPartEditor) //if (nFlags & MK_CONTROL) { { - base->ClearSnapPoint(); if (base->ClearHighlightedVertex()) _canUseMainBuffer = false; diff --git a/src/Control/Map_Events.cpp b/src/Control/Map_Events.cpp index b3916db6..2d5f0ee7 100644 --- a/src/Control/Map_Events.cpp +++ b/src/Control/Map_Events.cpp @@ -1258,7 +1258,10 @@ void CMapView::OnMouseMove(UINT nFlags, CPoint point) snapped = this->FindSnapPointCore(point.x, point.y, &x, &y); if (snapped) { ProjToPixel(x, y, &x, &y); + _shapeEditor->GetActiveShape()->SetSnapPoint(x, y, true); } + else + _shapeEditor->GetActiveShape()->ClearSnapPoint(); } shp->SetMousePosition(x, y); refreshNeeded = true; diff --git a/src/Control/Map_Identifier.cpp b/src/Control/Map_Identifier.cpp index ddb49124..a806125f 100644 --- a/src/Control/Map_Identifier.cpp +++ b/src/Control/Map_Identifier.cpp @@ -39,8 +39,6 @@ VARIANT_BOOL CMapView::FindSnapPoint(double tolerance, double xScreen, double yS double distance; double minDist = DBL_MAX; - long foundShapeIndex; - long foundPointIndex; bool digitizing = EditorHelper::IsDigitizingCursor((tkCursorMode)m_cursorMode); tkLayerSelection behavior; @@ -70,34 +68,39 @@ VARIANT_BOOL CMapView::FindSnapPoint(double tolerance, double xScreen, double yS sf->get_Snappable(&snappable); if (snappable) { + double xF, yF; + bool snapped = false; // Try snapping to a vertex first: if (mode == smVertices || mode == smVerticesAndLines) { sf->GetClosestVertex(x, y, maxDist, &shapeIndex, &pointIndex, &distance, &vb); if (vb && distance < minDist) { IShape* shape = NULL; - sf->get_Shape(foundShapeIndex, &shape); + sf->get_Shape(shapeIndex, &shape); if (shape) { minDist = distance; - shape->get_XY(foundPointIndex, xFound, yFound, &vb); + shape->get_XY(pointIndex, &xF, &yF, &vb); shape->Release(); - result = true; + snapped = true; } } } // If not snapped to a vertex, maybe try to snap to a segment: - if (!result && (mode == smVerticesAndLines || mode == smLines)) { - double xF, yF; + if (!snapped && (mode == smVerticesAndLines || mode == smLines)) { sf->GetClosestSnapPosition(x, y, maxDist, &shapeIndex, &xF, &yF, &distance, &vb); if (vb && distance < minDist) { minDist = distance; - *xFound = xF; - *yFound = yF; - result = true; + snapped = true; } } + + if (snapped) { + *xFound = xF; + *yFound = yF; + result = true; + } } } } From 06c9ade158064e18c7d6a7831eb6f5c4e8314fcb Mon Sep 17 00:00:00 2001 From: Mathijs Dumon Date: Thu, 24 Jan 2019 14:15:52 +0100 Subject: [PATCH 3/3] Changed segment snapping - using only mapwingis/GEOS methods --- src/ComHelpers/ShapefileHelper.cpp | 98 ++++++++++++++---------------- src/Control/Map_Edit.cpp | 10 ++- src/Editor/ActiveShape.h | 4 +- 3 files changed, 53 insertions(+), 59 deletions(-) diff --git a/src/ComHelpers/ShapefileHelper.cpp b/src/ComHelpers/ShapefileHelper.cpp index 84510062..84ee41f9 100644 --- a/src/ComHelpers/ShapefileHelper.cpp +++ b/src/ComHelpers/ShapefileHelper.cpp @@ -396,74 +396,64 @@ bool ShapefileHelper::GetClosestSnapPosition(IShapefile* sf, double x, double y, VARIANT_BOOL vb; double minDist = DBL_MAX; + + IPoint* pnt = NULL; + IShape* ptShp = NULL; + IShape* resShp = NULL; + for (long id : ids) { VARIANT_BOOL visible; sf->get_ShapeVisible(id, &visible); if (!visible) continue; - IShape* shp = nullptr; + IShape* shp = NULL; sf->get_Shape(id, &shp); - if (shp) + + if (shp != NULL) { - long numPoints; - shp->get_NumPoints(&numPoints); - double xSeg, ySeg; - - for (long j = 0; j < numPoints; j++) - { - if (j + 1 >= numPoints) { - shp->get_XY(j, &xSeg, &ySeg, &vb); - } - else { - double xPnt1, yPnt1; - double xPnt2, yPnt2; - shp->get_XY(j, &xPnt1, &yPnt1, &vb); - shp->get_XY(j+1, &xPnt2, &yPnt2, &vb); - ShapefileHelper::GetClosestPointOnSegment(xPnt1, yPnt1, xPnt2, yPnt2, x, y, &xSeg, &ySeg); - } - const double distance = sqrt(pow(x - xSeg, 2.0) + pow(y - ySeg, 2.0)); - if (distance < minDist && distance < maxDistance) - { - minDist = distance; - *shapeIndex = id; - fx = xSeg; - fy = ySeg; - } - } - shp->Release(); + // Create point shape + ComHelper::CreateShape(&ptShp); + ptShp->Create(SHP_POINT, &vb); + if (vb != VARIANT_TRUE) + { + shp->Release(); + pnt->Release(); + continue; + } + + // Add point + ComHelper::CreatePoint(&pnt); + pnt->put_X(x); + pnt->put_Y(y); + long position = 0; + ptShp->InsertPoint(pnt, &position, &vb); + pnt->Release(); + + // Get closest points: + shp->ClosestPoints(ptShp , &resShp); + ptShp->Release(); + shp->Release(); + + if (resShp != NULL) { + // Get the point snapped on geometry + double xPnt, yPnt; + resShp->get_XY(0, &xPnt, &yPnt, &vb); + + // Check if this is allowed and/or smaller than the previous found point: + const double distance = sqrt(pow(x - xPnt, 2.0) + pow(y - yPnt, 2.0)); + if (distance < minDist && distance < maxDistance) { + fx = xPnt; + fy = yPnt; + minDist = distance; + } + } } } dist = minDist; return minDist < maxDistance; } -// ***************************************************************** -// GetClosestPoint() -// ***************************************************************** -bool ShapefileHelper::GetClosestPointOnSegment(double ax, double ay, double bx, double by, double px, double py, - double *rx, double *ry) { - - double apx = px - ax; - double apy = py - ay; - double abx = bx - ax; - double aby = by - ay; - - double ab2 = abx * abx + aby * aby; - double ap_ab = apx * abx + apy * aby; - double t = ap_ab / ab2; - if (t < 0) { - t = 0; - } - else if (t > 1) { - t = 1; - } - *rx = ax + abx * t; - *ry = ay + aby * t; - - return true; -} - // ******************************************************************** // PointInPolygon() // ******************************************************************** diff --git a/src/Control/Map_Edit.cpp b/src/Control/Map_Edit.cpp index 049bec53..7d35e7ef 100644 --- a/src/Control/Map_Edit.cpp +++ b/src/Control/Map_Edit.cpp @@ -82,13 +82,17 @@ bool CMapView::HandleOnMouseMoveShapeEditor(int x, int y, long nFlags) _shapeEditor->get_SnapBehavior(&behavior); double projX, projY; - bool snapped = FindSnapPointCore(x, y, &projX, &projY); + VARIANT_BOOL snapped = FindSnapPointCore(x, y, &projX, &projY); if (!snapped) { PixelToProjection(x, y, projX, projY); GetEditorBase()->ClearSnapPoint(); } - else - GetEditorBase()->SetSnapPoint(projX, projY, true); + else { + double sX, sY; + ProjToPixel(projX, projY, &sX, &sY); + GetEditorBase()->SetSnapPoint(sX, sY, true); + } + if ((_dragging.Operation == DragMoveVertex || _dragging.Operation == DragMoveShape || diff --git a/src/Editor/ActiveShape.h b/src/Editor/ActiveShape.h index d5b8a75d..5be462e1 100644 --- a/src/Editor/ActiveShape.h +++ b/src/Editor/ActiveShape.h @@ -95,8 +95,8 @@ class ActiveShape: public GeoShape int _selectedPart; int _highlightedPart; bool _showSnapPoint; - int _snapPointX; - int _snapPointY; + double _snapPointX; + double _snapPointY; public: