using System; using System.Collections.Generic; using System.Text; namespace OSGeo.MapGuide.Viewer.Redlining { /// /// A callback that's invoked when a new redline object is added /// /// The key of the inserted feature. Some providers may not be able to return the key of the inserted feature and will pass null into this callback /// The text of the redline public delegate void RedlineAction(int? key, string text); /// /// A callback that retrieves the redline text /// /// public delegate string RedlineTextFunction(); internal class RedlineEditor { private IMapViewer _viewer; private RedlineLayer _layer; private MgFeatureService _featSvc; private MgAgfReaderWriter _agfRW; private MgWktReaderWriter _wktRW; private MgGeometryFactory _geomFact; public RedlineEditor(IMapViewer viewer, RedlineLayer layer) { _viewer = viewer; _layer = layer; var provider = viewer.GetProvider(); _featSvc = (MgFeatureService)provider.CreateService(MgServiceType.FeatureService); _wktRW = new MgWktReaderWriter(); _agfRW = new MgAgfReaderWriter(); _geomFact = new MgGeometryFactory(); } public IEnumerable GetAllFeatures() { MgMapBase map = _viewer.GetMap(); MgLayerCollection layers = map.GetLayers(); MgLayerBase redlineLayer = layers.GetItem(_layer.SystemName); MgResourceIdentifier resId = new MgResourceIdentifier(redlineLayer.GetFeatureSourceId()); MgFeatureReader reader = null; try { reader = _featSvc.SelectFeatures(resId, RedlineSchemaFactory.CLASS_NAME, null); //HACK: Another leaky abstraction. SHP will always choose FeatId, so once again //use the class definition to determine the identity property name MgClassDefinition cls = reader.GetClassDefinition(); MgPropertyDefinitionCollection idProps = cls.GetIdentityProperties(); MgPropertyDefinition keyProp = idProps.GetItem(0); string idName = keyProp.GetName(); while (reader.ReadNext()) { int id = reader.GetInt32(idName); string text = reader.IsNull(RedlineSchemaFactory.TEXT_NAME) ? string.Empty : reader.GetString(RedlineSchemaFactory.TEXT_NAME); yield return new RedlineObject(id, text); } } finally { if (reader != null) reader.Close(); } } public void AddObject(RedlineGeometryType type, RedlineTextFunction retrieveTextMethod, RedlineAction onRedlineAdded) { switch (type) { case RedlineGeometryType.Point: AddPoint(retrieveTextMethod, onRedlineAdded); break; case RedlineGeometryType.Circle: AddCircle(retrieveTextMethod, onRedlineAdded); break; case RedlineGeometryType.Line: AddLine(retrieveTextMethod, onRedlineAdded); break; case RedlineGeometryType.LineString: AddLineString(retrieveTextMethod, onRedlineAdded); break; case RedlineGeometryType.Polygon: AddPolygon(retrieveTextMethod, onRedlineAdded); break; case RedlineGeometryType.Rectangle: AddRectangle(retrieveTextMethod, onRedlineAdded); break; } } internal void DeleteRedlines(int[] ids) { MgMapBase map = _viewer.GetMap(); MgLayerCollection layers = map.GetLayers(); MgLayerBase redlineLayer = layers.GetItem(_layer.SystemName); //HACK: Workaround FeatId leaky abstraction in SHP provider MgClassDefinition cls = redlineLayer.GetClassDefinition(); MgPropertyDefinitionCollection idProps = cls.GetIdentityProperties(); MgPropertyDefinition idProp = idProps.GetItem(0); redlineLayer.ForceRefresh(); //This lib doesn't reference mg-desktop so the convenience APIs aren't available to us //Gotta go the old verbose route List filters = new List(); foreach (int id in ids) { filters.Add(idProp.Name + " = " + id); } string deleteFilter = string.Join(" OR ", filters.ToArray()); MgFeatureCommandCollection commands = new MgFeatureCommandCollection(); MgDeleteFeatures delete = new MgDeleteFeatures(redlineLayer.FeatureClassName, deleteFilter); commands.Add(delete); MgPropertyCollection result = redlineLayer.UpdateFeatures(commands); MgInt32Property deleteResult = result.GetItem(0) as MgInt32Property; if (deleteResult != null && deleteResult.GetValue() > 0) { _viewer.RefreshMap(); } } internal void UpdateRedlineText(string text, int[] ids) { MgMapBase map = _viewer.GetMap(); MgLayerCollection layers = map.GetLayers(); MgLayerBase redlineLayer = layers.GetItem(_layer.SystemName); //HACK: Workaround FeatId leaky abstraction in SHP provider MgClassDefinition cls = redlineLayer.GetClassDefinition(); MgPropertyDefinitionCollection idProps = cls.GetIdentityProperties(); MgPropertyDefinition idProp = idProps.GetItem(0); redlineLayer.ForceRefresh(); //This lib doesn't reference mg-desktop so the convenience APIs aren't available to us //Gotta go the old verbose route List filters = new List(); foreach (int id in ids) { filters.Add(idProp.Name + " = " + id); } string updateFilter = string.Join(" OR ", filters.ToArray()); MgFeatureCommandCollection commands = new MgFeatureCommandCollection(); MgPropertyCollection updateValues = new MgPropertyCollection(); MgStringProperty updateProp = new MgStringProperty(RedlineSchemaFactory.TEXT_NAME, text); updateValues.Add(updateProp); MgUpdateFeatures update = new MgUpdateFeatures(redlineLayer.FeatureClassName, updateValues, updateFilter); commands.Add(update); MgPropertyCollection result = redlineLayer.UpdateFeatures(commands); MgInt32Property updateResult = result.GetItem(0) as MgInt32Property; MgStringProperty errorResult = result.GetItem(0) as MgStringProperty; if (errorResult != null) { throw new Exception(errorResult.GetValue()); } _viewer.RefreshMap(); } private void InsertRedlineGeometry(string text, MgGeometry geom, RedlineAction onRedlineAdded) { MgPropertyCollection feature = new MgPropertyCollection(); MgByteReader agf = _agfRW.Write(geom); MgGeometryProperty geomProp = new MgGeometryProperty(RedlineSchemaFactory.GEOM_NAME, agf); feature.Add(geomProp); MgStringProperty strProp = new MgStringProperty(RedlineSchemaFactory.TEXT_NAME, text); feature.Add(strProp); MgMapBase map = _viewer.GetMap(); MgLayerCollection layers = map.GetLayers(); MgLayerBase redlineLayer = layers.GetItem(_layer.SystemName); MgClassDefinition cls = redlineLayer.GetClassDefinition(); MgPropertyDefinitionCollection idProps = cls.GetIdentityProperties(); MgPropertyDefinition idProp = idProps.GetItem(0); redlineLayer.ForceRefresh(); //This lib doesn't reference mg-desktop so the convenience APIs aren't available to us //Gotta go the old verbose route MgFeatureCommandCollection commands = new MgFeatureCommandCollection(); MgInsertFeatures insert = new MgInsertFeatures(redlineLayer.FeatureClassName, feature); commands.Add(insert); MgPropertyCollection result = redlineLayer.UpdateFeatures(commands); //Insert result is a MgFeatureProperty containing an MgFeatureReader MgFeatureProperty insertResult = result.GetItem(0) as MgFeatureProperty; MgStringProperty errorResult = result.GetItem(0) as MgStringProperty; if (insertResult != null) { var reader = insertResult.GetValue(); int inserted = 0; int? id = null; try { if (reader.ReadNext()) { inserted++; id = reader.GetInt32(idProp.Name); } } catch (MgException ex) { ex.Dispose(); } finally { reader.Close(); } if (inserted > 0) { _viewer.RefreshMap(); onRedlineAdded(id, text); } } else if (errorResult != null) { throw new Exception(errorResult.GetValue()); } } private void AddRectangle(RedlineTextFunction retrieveTextMethod, RedlineAction onRedlineAdded) { _viewer.DigitizeRectangle((x1, y1, x2, y2) => { string text = string.Empty; if (retrieveTextMethod != null) text = retrieveTextMethod(); MgGeometry geom = _wktRW.Read(Util.MakeWktPolygon(x1, y1, x2, y2)); InsertRedlineGeometry(text, geom, onRedlineAdded); }); } private void AddPolygon(RedlineTextFunction retrieveTextMethod, RedlineAction onRedlineAdded) { _viewer.DigitizePolygon((coordinates) => { string text = string.Empty; if (retrieveTextMethod != null) text = retrieveTextMethod(); MgCoordinateCollection coords = new MgCoordinateCollection(); for (int i = 0; i < coordinates.GetLength(0); i++) { coords.Add(_geomFact.CreateCoordinateXY(coordinates[i, 0], coordinates[i, 1])); } coords.Add(_geomFact.CreateCoordinateXY(coordinates[0, 0], coordinates[0, 1])); MgLinearRing ring = _geomFact.CreateLinearRing(coords); MgGeometry poly = _geomFact.CreatePolygon(ring, null); InsertRedlineGeometry(text, poly, onRedlineAdded); }); } private void AddLineString(RedlineTextFunction retrieveTextMethod, RedlineAction onRedlineAdded) { _viewer.DigitizeLineString((coordinates) => { string text = string.Empty; if (retrieveTextMethod != null) text = retrieveTextMethod(); MgCoordinateCollection coords = new MgCoordinateCollection(); for (int i = 0; i < coordinates.GetLength(0); i++) { coords.Add(_geomFact.CreateCoordinateXY(coordinates[i, 0], coordinates[i, 1])); } MgGeometry line = _geomFact.CreateLineString(coords); InsertRedlineGeometry(text, line, onRedlineAdded); }); } private void AddLine(RedlineTextFunction retrieveTextMethod, RedlineAction onRedlineAdded) { _viewer.DigitizeLine((x1, y1, x2, y2) => { string text = string.Empty; if (retrieveTextMethod != null) text = retrieveTextMethod(); MgCoordinateCollection coords = new MgCoordinateCollection(); coords.Add(_geomFact.CreateCoordinateXY(x1, y1)); coords.Add(_geomFact.CreateCoordinateXY(x2, y2)); MgGeometry line = _geomFact.CreateLineString(coords); InsertRedlineGeometry(text, line, onRedlineAdded); }); } private void AddPoint(RedlineTextFunction retrieveTextMethod, RedlineAction onRedlineAdded) { _viewer.DigitizePoint((x, y) => { string text = string.Empty; if (retrieveTextMethod != null) text = retrieveTextMethod(); MgCoordinate coord = _geomFact.CreateCoordinateXY(x, y); MgGeometry point = _geomFact.CreatePoint(coord); InsertRedlineGeometry(text, point, onRedlineAdded); }); } private void AddCircle(RedlineTextFunction retrieveTextMethod, RedlineAction onRedlineAdded) { _viewer.DigitizeCircle((x, y, r) => { string text = string.Empty; if (retrieveTextMethod != null) text = retrieveTextMethod(); MgGeometry geom = _wktRW.Read(Util.MakeWktCircle(x, y, r, true)); InsertRedlineGeometry(text, geom, onRedlineAdded); }); } } }