ViperPin tool
ViperPinForm.cs
// Copyright 2012 ESRI
// 
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
// 
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
// 
// See the use restrictions.
// 

using ESRI.ArcGIS.ArcMapUI;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Editor;
using ESRI.ArcGIS.Framework;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.esriSystem;
using System;
using System.Windows.Forms;
using Microsoft.Win32;

namespace ViperPin
{
  public partial class ViperPinForm : Form
  {
    private IEditor m_editor;
    private IPolyline m_curve;
    private IEditLayers m_editLayers;
    private IEditSketch3 m_edSketch;
    private int m_lotNum;

    public ViperPinForm(IEditor3 editor)
    {
      InitializeComponent();

      m_editor = editor;
      m_edSketch = m_editor as IEditSketch3;
      m_editLayers = m_editor as IEditLayers;

      lblEditLayer.Text = m_editLayers.CurrentLayer.Name;

      //Load field combo box with field names
      IFields pFields = m_editLayers.CurrentLayer.FeatureClass.Fields;
      for (int i=0; i < pFields.FieldCount; i++)
      {
        cmbPINField.Items.Add(pFields.get_Field(i).Name);
      }

      //get pinfield from registry
      string pinField = null;
      RegistryKey pRegKey = Registry.CurrentUser.OpenSubKey("Software\\ESRI\\ViperPin");
      if (pRegKey != null)
      {
        pinField = pRegKey.GetValue("Pinfield").ToString();
      }

      //set the combo box to the pinfield
      for (int i = 0; i < pFields.FieldCount; i++)
      {
        if (pinField == pFields.get_Field(i).Name)
        {
          cmbPINField.Text = pinField;
          break;
        }
        else
        {
          cmbPINField.Text = "None";
        }
      }

      //cmbPINField.SelectedIndex = 0;
      cmbPINField.Refresh();
      m_lotNum = 1;
      txtlot.Text = "1";

      //Set center right of form to center right of screen
      this.StartPosition = FormStartPosition.Manual;
      this.Left = 0;
      this.Top = (Screen.PrimaryScreen.Bounds.Height / 2) - (this.Height / 2);
    }

    private void cmdOK_Click(object sender, EventArgs e)
    {
      m_lotNum = int.Parse(txtlot.Text);

      //Set pin value
      SetPINValue();

      //save pinfield
      RegistryKey regKey = Registry.CurrentUser.OpenSubKey("Software",true);
      RegistryKey newKey = regKey.CreateSubKey("ESRI\\ViperPin");
      newKey.SetValue("Pinfield", cmbPINField.Text);

      this.Hide();
      
      //redraw labels
      //m_editor.Display.Invalidate(null, true, (short)esriViewDrawPhase.esriViewGraphics);

      IMxDocument mxDoc;
      mxDoc = m_editor.Parent.Document as IMxDocument;
      mxDoc.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null);
    }

    private void cmdCancel_Click(object sender, EventArgs e)
    {
      this.Close();
    }

    private void SetPINValue()
    {
      //The Theory.
      //Select polygons that intersect the sketch.
      //Construct one polyline from the boundaries and intersect with sketch.
      //Sort resulting intersection locations (multipoint) by distance of the intersect
      // from the start of the sketch and create new ordered multipoint.
      //Loop through new ordered multipoint, select underlying parcel and calc pin.

      IFeatureLayer featLayer = m_editLayers.CurrentLayer;
      m_curve = m_edSketch.Geometry as IPolyline;

      //Search parcel polys by graphic to get feature cursor
      ISpatialFilter spatialFilter = new SpatialFilterClass();
      spatialFilter.Geometry = m_curve;
      spatialFilter.GeometryField = m_editLayers.CurrentLayer.FeatureClass.ShapeFieldName;
      spatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelCrosses;

      IFeatureCursor featCursor = featLayer.Search(spatialFilter,true);
      IFeature feature = featCursor.NextFeature();

      //If we have no intersects then exit
      if (feature == null)
        return;
  
      //Make a GeomBag of the polygons boundaries (polylines)
      IGeometryCollection geomBag = new GeometryBagClass();
      object missing = Type.Missing;
      while (feature != null)
      {
        ITopologicalOperator poly = feature.Shape as ITopologicalOperator;
        geomBag.AddGeometry(poly.Boundary,ref missing,ref missing);
        feature = featCursor.NextFeature();
      }

      //Make one polyline from the boundaries
      IPolyline polyLineU = new PolylineClass();
      ITopologicalOperator topoOp = polyLineU as ITopologicalOperator;
      topoOp.ConstructUnion(geomBag as IEnumGeometry);

      //Get the intersections of the boundaries and the curve
      IPointCollection pointCol = topoOp.Intersect(m_curve, esriGeometryDimension.esriGeometry0Dimension) as IPointCollection;

      //The point collection is not ordered by distance along the curve so
      //need to create a new collection with this info

      int[] pointOrder = new int[pointCol.PointCount];
      double dac = 0, dfc = 0;
      bool bRS = false;

      for (int i = 0; i < pointCol.PointCount; i++)
      {
        IPoint queryPoint = new PointClass();
        pointCol.QueryPoint(i, queryPoint);
        m_curve.QueryPointAndDistance(esriSegmentExtension.esriNoExtension, queryPoint, false, null,ref dac,ref dfc,ref bRS);
        pointOrder[i] = (int)dac;
      }

      //use built in bubble sort
      System.Array.Sort(pointOrder);

      //Loop through the sorted array and calc midpoint between parcel boundaries
      IPointCollection midPoints = new MultipointClass();

      for (int i = 0; i < pointOrder.Length -1; i++)
      {
        //Get the midpoint distance
        double midPointDist = (pointOrder[i] + pointOrder[i + 1]) / 2;
        
        //create a point at the distance and store in point collection
        IPoint queryPoint = new PointClass();
        m_curve.QueryPoint(esriSegmentExtension.esriNoExtension, midPointDist, false, queryPoint);
        midPoints.AddPoint(queryPoint,ref missing,ref missing);
      }

      //If ends of sketch are included then add them as points
      if (chkEnds.Checked)
      {
        object before = 0 as object;
        midPoints.AddPoint(m_curve.FromPoint, ref before, ref missing);
        midPoints.AddPoint(m_curve.ToPoint, ref missing, ref missing);
      }

      m_editor.StartOperation();

      //Loop through calculated midpoints, select polygon and calc pin
      for (int i = 0; i < midPoints.PointCount; i++)
      {
        IPoint midPoint = midPoints.get_Point(i);
        spatialFilter = new SpatialFilterClass();
        spatialFilter.Geometry = midPoint;
        spatialFilter.GeometryField = m_editLayers.CurrentLayer.FeatureClass.ShapeFieldName;
        spatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelWithin;

        featCursor = featLayer.Search(spatialFilter, false);
        while ((feature = featCursor.NextFeature()) != null)
        {
          feature.set_Value(feature.Fields.FindField(cmbPINField.Text), m_lotNum);
          feature.Store();
          m_lotNum += int.Parse(txtlotinc.Text);
        }
      }
      m_editor.StopOperation("ViperPIN");
      txtlot.Text = m_lotNum.ToString();
    }
  }
}