/// <summary> /// Creates a new instance of DrawArgs /// </summary> public MapDrawArgs(Graphics inGraphics, Rectangle clipRectangle, IMapFrame inMapFrame) { _graphics = inGraphics; _geoGraphics = new MapArgs(clipRectangle, inMapFrame.Extents); _clipRectangle = clipRectangle; }
public void DrawRegions(MapArgs args, List<IEnvelope> regions) { // To get information on the pixel resolution you can use Rectangle mapRegion = args.ImageRectangle; // Handle tile management here based on geographic extent and pixel resolution from mapRegion }
// This draws the individual line features private void DrawFeatures(MapArgs e, IEnumerable<int> indices) { Graphics g = e.Device ?? Graphics.FromImage(_backBuffer); double minX = e.MinX; double maxY = e.MaxY; double dx = e.Dx; double dy = e.Dy; if(!DrawnStatesNeeded) { if(Symbology == null || Symbology.Categories.Count == 0) return; FastDrawnState state = new FastDrawnState(false, Symbology.Categories[0]); IPointCategory pc = state.Category as IPointCategory; IPointSymbolizer ps = null; if(pc != null && pc.Symbolizer !=null) ps = pc.Symbolizer; if (ps == null) return; g.SmoothingMode = ps.Smoothing ? SmoothingMode.AntiAlias : SmoothingMode.None; double[] vertices = DataSet.Vertex; foreach (int index in indices) { if(!DrawnStates[index].Visible) continue; if(DataSet.FeatureType == FeatureTypes.Point) { System.Drawing.Point pt = new System.Drawing.Point(); pt.X = Convert.ToInt32((vertices[index * 2] - minX) * dx); pt.Y = Convert.ToInt32((maxY - vertices[index * 2 + 1]) * dy); double scaleSize = 1; if (ps.ScaleMode == ScaleModes.Geographic) { scaleSize = e.ImageRectangle.Width / e.GeographicExtents.Width; } Matrix old = g.Transform; Matrix shift = g.Transform; shift.Translate(pt.X, pt.Y); g.Transform = shift; ps.Draw(g, scaleSize); g.Transform = old; } else { // multi-point ShapeRange range = DataSet.ShapeIndices[index]; for (int i = range.StartIndex; i <= range.EndIndex(); i++) { System.Drawing.Point pt = new System.Drawing.Point(); pt.X = Convert.ToInt32((vertices[i * 2 ] - minX) * dx); pt.Y = Convert.ToInt32((maxY - vertices[i * 2 + 1]) * dy); double scaleSize = 1; if (ps.ScaleMode == ScaleModes.Geographic) { scaleSize = e.ImageRectangle.Width / e.GeographicExtents.Width; } Matrix old = g.Transform; Matrix shift = g.Transform; shift.Translate(pt.X, pt.Y); g.Transform = shift; ps.Draw(g, scaleSize); g.Transform = old; } } } } else { FastDrawnState[] states = DrawnStates; double[] vertices = DataSet.Vertex; foreach (IPointCategory category in Symbology.Categories) { if (category.Symbolizer == null) continue; double scaleSize = 1; if (category.Symbolizer.ScaleMode == ScaleModes.Geographic) { scaleSize = e.ImageRectangle.Width / e.GeographicExtents.Width; } Size2D size = category.Symbolizer.GetSize(); if (size.Width * scaleSize < 1 || size.Height * scaleSize < 1) continue; Bitmap normalSymbol = new Bitmap((int)(size.Width * scaleSize), (int)(size.Height * scaleSize)); Graphics bg = Graphics.FromImage(normalSymbol); bg.SmoothingMode = category.Symbolizer.Smoothing ? SmoothingMode.AntiAlias : SmoothingMode.None; Matrix trans = bg.Transform; trans.Translate((float)size.Width/2, (float)size.Height/2); bg.Transform = trans; category.Symbolizer.Draw(bg, 1); Size2D selSize = category.SelectionSymbolizer.GetSize(); if (selSize.Width * scaleSize < 1 || selSize.Height * scaleSize < 1) continue; Bitmap selectedSymbol = new Bitmap((int)(size.Width * scaleSize), (int)(size.Height * scaleSize)); Graphics sg = Graphics.FromImage(selectedSymbol); sg.SmoothingMode = category.SelectionSymbolizer.Smoothing ? SmoothingMode.AntiAlias : SmoothingMode.None; Matrix trans2 = bg.Transform; trans2.Translate((float)size.Width / 2, (float)size.Height / 2); sg.Transform = trans2; category.Symbolizer.Draw(sg, 1); foreach (int index in indices) { FastDrawnState state = states[index]; if (!state.Visible) continue; if (state.Category == null) continue; IPointCategory pc = state.Category as IPointCategory; if (pc == null) continue; if (pc != category) continue; Bitmap bmp = normalSymbol; if (state.Selected) { bmp = selectedSymbol; } if (DataSet.FeatureType == FeatureTypes.Point) { System.Drawing.Point pt = new System.Drawing.Point(); pt.X = Convert.ToInt32((vertices[index * 2] - minX) * dx); pt.Y = Convert.ToInt32((maxY - vertices[index * 2 + 1]) * dy); Matrix old = g.Transform; Matrix shift = g.Transform; shift.Translate(pt.X, pt.Y); g.Transform = shift; g.DrawImageUnscaled(bmp, -bmp.Width/2,-bmp.Height/2); g.Transform = old; } else { ShapeRange range = DataSet.ShapeIndices[index]; for (int i = range.StartIndex; i <= range.EndIndex(); i++) { System.Drawing.Point pt = new System.Drawing.Point(); pt.X = Convert.ToInt32((vertices[i*2] - minX)*dx); pt.Y = Convert.ToInt32((maxY - vertices[i*2 + 1])*dy); Matrix old = g.Transform; Matrix shift = g.Transform; shift.Translate(pt.X, pt.Y); g.Transform = shift; g.DrawImageUnscaled(bmp, -bmp.Width / 2, -bmp.Height / 2); g.Transform = old; } } } } if (e.Device == null) g.Dispose(); } }
/// <summary> /// If useChunks is true, then this method /// </summary> /// <param name="args">The GeoArgs that control how these features should be drawn.</param> /// <param name="indices">The features that should be drawn.</param> /// <param name="clipRectangles">If an entire chunk is drawn and an update is specified, this clarifies the changed rectangles.</param> /// <param name="useChunks">Boolean, if true, this will refresh the buffer in chunks.</param> public virtual void DrawFeatures(MapArgs args, List<int> indices, List<Rectangle> clipRectangles, bool useChunks) { if (useChunks == false) { DrawFeatures(args, indices); return; } int count = indices.Count; int numChunks = (int)Math.Ceiling(count / (double)ChunkSize); for (int chunk = 0; chunk < numChunks; chunk++) { int numFeatures = ChunkSize; if (chunk == numChunks - 1) numFeatures = indices.Count - (chunk * ChunkSize); DrawFeatures(args, indices.GetRange(chunk * ChunkSize, numFeatures)); if (numChunks > 0 && chunk < numChunks - 1) { // FinishDrawing(); OnBufferChanged(clipRectangles); System.Windows.Forms.Application.DoEvents(); // this.StartDrawing(); } } }
/// <summary> /// Creates a new raster with the specified cell size. If the cell size /// is zero, this will default to the shorter of the width or height /// divided by 256. If the cell size produces a raster that is greater /// than 8,000 pixels in either dimension, it will be re-sized to /// create an 8,000 length or width raster. /// </summary> /// <param name="fs">The featureset to convert to a raster</param> /// <param name="cellSize">The double extent of the cell.</param> /// <param name="fieldName">The integer field index of the file.</param> /// <param name="destFilename">The filename of the raster to create</param> /// <param name="driverCode">The optional GDAL driver code to use if using GDAL /// for a format that is not discernable from the file extension. An empty string /// is usually perfectly acceptable here.</param> /// <param name="options">For GDAL rasters, they can be created with optional parameters /// passed in as a string array. In most cases an empty string is perfectly acceptable.</param> /// <param name="progressHandler">An interface for handling the progress messages.</param> /// <returns>Generates a raster from the vectors.</returns> public static IRaster ToRaster(IFeatureSet fs, ref double cellSize, string fieldName, string destFilename, string driverCode, string[] options, IProgressHandler progressHandler) { IEnvelope env = fs.Envelope; if(cellSize == 0) { if(fs.Envelope.Width < fs.Envelope.Height) { cellSize = env.Width/256; } else { cellSize = env.Height/256; } } int w = (int)Math.Ceiling(env.Width/cellSize); if (w > 8000) { w = 8000; cellSize = env.Width/8000; } int h = (int) Math.Ceiling(env.Height/cellSize); if (h > 8000) { cellSize = env.Height/8000; h = 8000; } Bitmap bmp = new Bitmap(w, h); Graphics g = Graphics.FromImage(bmp); g.Clear(Color.Transparent); g.SmoothingMode = SmoothingMode.None; g.TextRenderingHint = TextRenderingHint.SingleBitPerPixel; g.InterpolationMode = InterpolationMode.NearestNeighbor; Hashtable colorTable; MapArgs args = new MapArgs(new Rectangle(0, 0, w, h), env, g); switch (fs.FeatureType) { case FeatureTypes.Polygon: { MapPolygonLayer mpl = new MapPolygonLayer(fs); PolygonScheme ps = new PolygonScheme(); colorTable = ps.GenerateUniqueColors(fs, fieldName); mpl.Symbology = ps; mpl.DrawRegions(args, new List<IEnvelope> {env}); } break; case FeatureTypes.Line: { MapLineLayer mpl = new MapLineLayer(fs); LineScheme ps = new LineScheme(); colorTable = ps.GenerateUniqueColors(fs, fieldName); mpl.Symbology = ps; mpl.DrawRegions(args, new List<IEnvelope> { env }); } break; default: { MapPointLayer mpl = new MapPointLayer(fs); PointScheme ps = new PointScheme(); colorTable = ps.GenerateUniqueColors(fs, fieldName); mpl.Symbology = ps; mpl.DrawRegions(args, new List<IEnvelope> { env }); } break; } Type tp = fieldName == "FID" ? typeof(int) : fs.DataTable.Columns[fieldName].DataType; if (tp == typeof(string)) tp = typeof (double); // We will try to convert to double if it is a string Raster output = new Raster(); MWImageData image = new MWImageData(bmp, env); ProgressMeter pm = new ProgressMeter(progressHandler, "Converting To Raster Cells", h); output.CreateNew(destFilename, driverCode, w, h, 1, tp, options); output.Bounds = new RasterBounds(h, w, env); List<RcIndex> locations = new List<RcIndex>(); List<string> failureList = new List<string>(); for (int row = 0; row < h; row++) { for (int col = 0; col < w; col++) { Color c = image.GetColor(row, col); if (c.A == 0) { output.Value[row, col] = output.NoDataValue; } else { if (colorTable.ContainsKey(c) == false) { if (c.A < 125) { output.Value[row, col] = output.NoDataValue; continue; } // Use a color matching distance to pick the closest member object val = GetCellValue(w, h, row, col, image, c, colorTable, locations); output.Value[row, col] = GetDouble(val, failureList); } else { output.Value[row, col] = GetDouble(colorTable[c], failureList); } } } pm.CurrentValue = row; } const int maxIterations = 5; int iteration = 0; while(locations.Count > 0) { List<RcIndex> newLocations = new List<RcIndex>(); foreach (RcIndex location in locations) { object val = GetCellValue(w, h, location.Row, location.Column, image, image.GetColor(location.Row, location.Column), colorTable, newLocations); output.Value[location.Row, location.Column] = GetDouble(val, failureList); } locations = newLocations; iteration++; if(iteration > maxIterations) break; } pm.Reset(); return output; }
/// <summary> /// This will draw any features that intersect this region. To specify the features /// directly, use OnDrawFeatures. This will not clear existing buffer content. /// For that call Initialize instead. /// </summary> /// <param name="args">A GeoArgs clarifying the transformation from geographic to image space</param> /// <param name="regions">The geographic regions to draw</param> /// <returns>The list of rectangular areas that match the specified regions</returns> public void DrawRegions(MapArgs args, List<IEnvelope> regions) { List<Rectangle> clipRects = args.ProjToPixel(regions); DrawWindows(args, regions, clipRects); }
private static RectangleF PlaceLineLabel(IBasicGeometry lineString, SizeF labelSize, MapArgs e, ILabelSymbolizer symb) { ILineString ls = Geometry.FromBasicGeometry(lineString) as ILineString; if (ls == null) return RectangleF.Empty; Coordinate c; if (symb.LabelMethod == LabelMethod.Centroid) c = ls.Centroid.Coordinate; else if (symb.LabelMethod == LabelMethod.InteriorPoint) c = ls.InteriorPoint.Coordinate; else c = ls.Envelope.Center(); PointF adjustment = Position(symb, labelSize); float x = Convert.ToSingle((c.X - e.MinX)*e.Dx) + adjustment.X; float y = Convert.ToSingle((e.MaxY - c.Y)*e.Dy) + adjustment.Y; return new RectangleF(x, y, labelSize.Width, labelSize.Height); }
private static RectangleF PlacePointLabel(IBasicGeometry f, MapArgs e, SizeF labelSize, ILabelSymbolizer symb) { Coordinate c = f.GetBasicGeometryN(1).Coordinates[0]; if (e.GeographicExtents.Contains(c) == false) return RectangleF.Empty; PointF adjustment = Position(symb, labelSize); float x = Convert.ToSingle((c.X - e.MinX) * e.Dx) + adjustment.X; float y = Convert.ToSingle((e.MaxY - c.Y) * e.Dy) + adjustment.Y; return new RectangleF(x, y, labelSize.Width, labelSize.Height); }
private void BuildPaths(MapArgs e, IEnumerable<int> indices, out List<GraphicsPath> paths) { paths = new List<GraphicsPath>(); Extent drawExtents = new Extent(e.PixelToProj(_drawingBounds)); Dictionary<FastDrawnState, GraphicsPath> borders = new Dictionary<FastDrawnState, GraphicsPath>(); for (int selectState = 0; selectState < 2; selectState++) { foreach (IPolygonCategory category in Symbology.Categories) { FastDrawnState state = new FastDrawnState(selectState == 1, category); GraphicsPath border = new GraphicsPath(); borders.Add(state, border); paths.Add(border); } } List<ShapeRange> shapes = DataSet.ShapeIndices; double[] vertices = DataSet.Vertex; if(!DrawnStatesNeeded) { FastDrawnState state = new FastDrawnState(false, Symbology.Categories[0]); foreach (int shp in indices) { ShapeRange shape = shapes[shp]; if (!shape.Extent.Intersects(e.GeographicExtents)) continue; if(shp >= shapes.Count) continue; if (!borders.ContainsKey(state)) continue; if (drawExtents.Contains(shape.Extent)) { BuildPolygon(vertices, shapes[shp], borders[state], e, false); } else { BuildPolygon(vertices, shapes[shp], borders[state], e, true); } } } else { FastDrawnState[] states = DrawnStates; foreach (int shp in indices) { if (shp >= shapes.Count) continue; if (shp >= states.Length) { AssignFastDrawnStates(); states = DrawnStates; } if (states[shp].Visible == false) continue; ShapeRange shape = shapes[shp]; if (!shape.Extent.Intersects(e.GeographicExtents)) continue; if (drawExtents.Contains(shape.Extent)) { FastDrawnState state = states[shp]; if (!borders.ContainsKey(state)) continue; BuildPolygon(vertices, shapes[shp], borders[state], e, false); } else { FastDrawnState state = states[shp]; if (!borders.ContainsKey(state)) continue; BuildPolygon(vertices, shapes[shp], borders[state], e, true); } } } }
/// <summary> /// Draws the GraphicsPaths. Before we were effectively "re-creating" the same geometric /// </summary> /// <param name="e"></param> /// <param name="paths"></param> private void DrawPaths(MapArgs e, IList<GraphicsPath> paths) { Graphics g = e.Device ?? Graphics.FromImage(_backBuffer); int numCategories = Symbology.Categories.Count; if(!DrawnStatesNeeded && !EditMode) { IPolygonSymbolizer ps = Symbolizer; g.SmoothingMode = ps.Smoothing ? SmoothingMode.AntiAlias : SmoothingMode.None; Extent catBounds = new Extent(DataSet.Envelope); RectangleF bounds = new RectangleF(); bounds.X = Convert.ToSingle((catBounds.XMin - e.MinX) * e.Dx); bounds.Y = Convert.ToSingle((e.MaxY - catBounds.YMax) * e.Dy); float r = Convert.ToSingle((catBounds.XMax - e.MinX) * e.Dx); bounds.Width = r - bounds.X; float b = Convert.ToSingle((e.MaxY - catBounds.YMin) * e.Dy); bounds.Height = b - bounds.Y; foreach (IPattern pattern in ps.Patterns) { IGradientPattern gp = pattern as IGradientPattern; if (gp != null) { gp.Bounds = bounds; } pattern.FillPath(g, paths[0]); } double scale = 1; if (ps.ScaleMode == ScaleModes.Geographic) { scale = e.ImageRectangle.Width / e.GeographicExtents.Width; } foreach (IPattern pattern in ps.Patterns) { if (pattern.UseOutline) { pattern.DrawPath(g, paths[0], scale); } } } else { for (int selectState = 0; selectState < 2; selectState++) { int iCategory = 0; foreach (IPolygonCategory category in Symbology.Categories) { Extent catBounds; if (CategoryExtents.Keys.Contains(category)) { catBounds = CategoryExtents[category]; } else { catBounds = CalculateCategoryExtent(category); } if (catBounds == null) catBounds = new Extent(Envelope); RectangleF bounds = new RectangleF(); bounds.X = Convert.ToSingle((catBounds.XMin - e.MinX) * e.Dx); bounds.Y = Convert.ToSingle((e.MaxY - catBounds.YMax) * e.Dy); float r = Convert.ToSingle((catBounds.XMax - e.MinX) * e.Dx); bounds.Width = r - bounds.X; float b = Convert.ToSingle((e.MaxY - catBounds.YMin) * e.Dy); bounds.Height = b - bounds.Y; int index = selectState * numCategories + iCategory; // Define the symbology based on the category and selection state IPolygonSymbolizer ps = category.Symbolizer; if (selectState == SELECTED) ps = category.SelectionSymbolizer; g.SmoothingMode = ps.Smoothing ? SmoothingMode.AntiAlias : SmoothingMode.None; foreach (IPattern pattern in ps.Patterns) { IGradientPattern gp = pattern as IGradientPattern; if (gp != null) { gp.Bounds = bounds; } paths[index].FillMode = FillMode.Winding; pattern.FillPath(g, paths[index]); } double scale = 1; if (ps.ScaleMode == ScaleModes.Geographic) { scale = e.ImageRectangle.Width / e.GeographicExtents.Width; } foreach (IPattern pattern in ps.Patterns) { if (pattern.UseOutline) { pattern.DrawPath(g, paths[index], scale); } } iCategory++; } // category } // selectState } for (int i = 0; i < Symbology.Categories.Count; i++) { paths[i].Dispose(); } if(e.Device == null) g.Dispose(); }
// This draws the individual line features private void DrawFeatures(MapArgs e, IEnumerable<int> indices) { if (DataSet.ShapeIndices == null) return; List<GraphicsPath> paths; // First, use the coordinates to build the drawing paths BuildPaths(e, indices, out paths); // Next draw all the paths using the various category symbols. DrawPaths(e, paths); foreach (GraphicsPath path in paths) { path.Dispose(); } }
// This draws the individual line features private void DrawFeatures(MapArgs e, IEnumerable<IFeature> features) { List<GraphicsPath> paths; Stopwatch sw = new Stopwatch(); sw.Start(); // First, use the coordinates to build the drawing paths BuildPaths(e, features, out paths); // Next draw all the paths using the various category symbols. DrawPaths(e, paths); sw.Stop(); Debug.WriteLine("Drawing time: " + sw.ElapsedMilliseconds); foreach (GraphicsPath path in paths) { path.Dispose(); } }
/// <summary> /// This will draw any features that intersect this region. To specify the features /// directly, use OnDrawFeatures. This will not clear existing buffer content. /// For that call Initialize instead. /// </summary> /// <param name="args">A GeoArgs clarifying the transformation from geographic to image space</param> /// <param name="regions">The geographic regions to draw</param> /// <returns>The list of rectangular areas that match the specified regions</returns> public void DrawRegions(MapArgs args, List<IEnvelope> regions) { List<Rectangle> clipRects = args.ProjToPixel(regions); if (EditMode) { List<IFeature> drawList = new List<IFeature>(); foreach (IEnvelope region in regions) { if (region != null) { // Use union to prevent duplicates. No sense in drawing more than we have to. drawList = drawList.Union(DataSet.Select(region)).ToList(); } } DrawFeatures(args, drawList, clipRects, true); } else { List<int> drawList = new List<int>(); List<ShapeRange> shapes = DataSet.ShapeIndices; for (int shp = 0; shp < shapes.Count; shp++) { foreach (IEnvelope region in regions) { if (!shapes[shp].Extent.Intersects(region)) continue; drawList.Add(shp); break; } } DrawFeatures(args, drawList, clipRects, true); } }
/// <summary> /// Creates a new instance of GeoDrawArgs /// </summary> /// <param name="inGraphics"></param> /// <param name="clipRectangle"></param> /// <param name="inGeoGraphics"></param> public MapDrawArgs(Graphics inGraphics, Rectangle clipRectangle, MapArgs inGeoGraphics) { _graphics = inGraphics; _clipRectangle = clipRectangle; }
private static RectangleF PlacePolygonLabel(IBasicGeometry geom, MapArgs e, SizeF labelSize, ILabelSymbolizer symb) { IPolygon pg = Geometry.FromBasicGeometry(geom) as IPolygon; if (pg == null) return RectangleF.Empty; Coordinate c; switch (symb.LabelMethod) { case LabelMethod.Centroid: c = pg.Centroid.Coordinates[0]; break; case LabelMethod.InteriorPoint: c = pg.InteriorPoint.Coordinate; break; default: c = geom.Envelope.Center(); break; } if (e.GeographicExtents.Contains(c) == false) return RectangleF.Empty; PointF adjustment = Position(symb, labelSize); float x = Convert.ToSingle((c.X - e.MinX) * e.Dx) + adjustment.X; float y = Convert.ToSingle((e.MaxY - c.Y) * e.Dy) + adjustment.Y; RectangleF result = new RectangleF(x, y, labelSize.Width, labelSize.Height); return result; }
private void BuildPaths(MapArgs e, IEnumerable<IFeature> features, out List<GraphicsPath> borderPaths) { borderPaths = new List<GraphicsPath>(); IEnvelope drawExtents = e.PixelToProj(_drawingBounds); for (int selectState = 0; selectState < 2; selectState++) { foreach (IPolygonCategory category in Symbology.Categories) { // Determine the subset of the specified features that are visible and match the category IPolygonCategory polygonCategory = category; int i = selectState; Func<IDrawnState, bool> isMember = state => state.SchemeCategory == polygonCategory && state.IsVisible && state.IsSelected == (i == 1); var drawnFeatures = from feature in features where isMember(DrawingFilter[feature]) select feature; GraphicsPath borderPath = new GraphicsPath(); foreach (IFeature f in drawnFeatures) { if (drawExtents.Contains(f.Envelope)) { // This optimization only works if we are zoomed out far enough // that the whole linestring fits in the short integer // drawing window. Otherwise, we need to crop the line. // FastBuildLine(graphPath, f, minX, maxY, dx, dy); BuildPolygon(DataSet.Vertex, f.ShapeIndex, borderPath, e, false); } else { BuildPolygon(DataSet.Vertex, f.ShapeIndex, borderPath, e, true); } } borderPaths.Add(borderPath); } } }
private static void DrawPointFeature(MapArgs e, Graphics g, IFeature f, ILabelCategory category, bool selected, List<RectangleF> existingLabels) { ILabelSymbolizer symb = category.Symbolizer; if (selected) symb = category.SelectionSymbolizer; //Gets the features text and calculate the label size string txt = GetLabelText(f, category); if (txt == null) return; SizeF labelSize = g.MeasureString(txt, symb.GetFont()); //Depending on the labeling strategy we do diff things if (symb.LabelParts == LabelParts.LabelAllParts) { for (int n = 0; n < f.NumGeometries; n++) { RectangleF labelBounds = PlacePointLabel(f, e, labelSize, symb); CollisionDraw(txt, g, symb, e, labelBounds, existingLabels); } } else { RectangleF labelBounds = PlacePointLabel(f, e, labelSize, symb); CollisionDraw(txt, g, symb, e, labelBounds, existingLabels); } }
/// <summary> /// Appends the specified polygon to the graphics path. /// </summary> private static void BuildPolygon(double[] vertices, ShapeRange shpx, GraphicsPath borderPath, MapArgs args, bool clip) { double minX = args.MinX; double maxY = args.MaxY; double dx = args.Dx; double dy = args.Dy; borderPath.FillMode = FillMode.Winding; for (int prt = 0; prt < shpx.Parts.Count; prt++) { PartRange prtx = shpx.Parts[prt]; int start = prtx.StartIndex; int end = prtx.EndIndex; List<double[]> points = new List<double[]>(); for (int i = start; i <= end; i++) { double[] pt = new double[2]; pt[X] = (vertices[i * 2] - minX) * dx; pt[Y] = (maxY - vertices[i*2+1]) * dy; points.Add(pt); } if (clip) { points = SoutherlandHodgman.Clip(points); } List<System.Drawing.Point> intPoints = DuplicationPreventer.Clean(points); if (intPoints.Count < 2) { points.Clear(); continue; } borderPath.StartFigure(); System.Drawing.Point[] pointArray = intPoints.ToArray(); borderPath.AddLines(pointArray); points.Clear(); } }
private static void DrawLineFeature(MapArgs e, Graphics g, IFeature f, ILabelCategory category, bool selected, List<RectangleF> existingLabels) { ILabelSymbolizer symb = category.Symbolizer; if (selected) symb = category.SelectionSymbolizer; //Gets the features text and calculate the label size string txt = GetLabelText(f, category); if (txt == null) return; SizeF labelSize = g.MeasureString(txt, symb.GetFont()); if(f.NumGeometries == 1) { RectangleF labelBounds = PlaceLineLabel(f.BasicGeometry, labelSize, e, symb); CollisionDraw(txt, g, symb, e, labelBounds, existingLabels); } else { //Depending on the labeling strategy we do diff things if (symb.LabelParts == LabelParts.LabelAllParts) { for (int n = 0; n < f.NumGeometries; n++) { RectangleF labelBounds = PlaceLineLabel(f.GetBasicGeometryN(n), labelSize, e, symb); CollisionDraw(txt, g, symb, e, labelBounds, existingLabels); } } else { double longestLine = 0; int longestIndex = 0; for (int n = 0; n < f.NumGeometries; n++) { ILineString ls = f.GetBasicGeometryN(n) as ILineString; double tempLength = 0; if (ls != null) tempLength = ls.Length; if (longestLine < tempLength) { longestLine = tempLength; longestIndex = n; } } RectangleF labelBounds = PlaceLineLabel(f.GetBasicGeometryN(longestIndex), labelSize, e, symb); CollisionDraw(txt, g, symb, e, labelBounds, existingLabels); } } }
/// <summary> /// This will draw any features that intersect this region. To specify the features /// directly, use OnDrawFeatures. This will not clear existing buffer content. /// For that call Initialize instead. /// </summary> /// <param name="args">A GeoArgs clarifying the transformation from geographic to image space</param> /// <param name="regions">The geographic regions to draw</param> public void DrawRegions(MapArgs args, List<IEnvelope> regions) { if (FeatureSet == null) return; if(FeatureSet.IndexMode) { // First determine the number of features we are talking about based on region. List<int> drawIndices = new List<int>(); foreach (IEnvelope region in regions) { if (region != null) { // We need to consider labels that go off the screen. figure a region // that is larger. IEnvelope sur = new Envelope(region.Copy()); sur.ExpandBy(region.Width, region.Height); Extent r = new Extent(sur); // Use union to prevent duplicates. No sense in drawing more than we have to. drawIndices = drawIndices.Union(FeatureSet.SelectIndices(r)).ToList(); } } List<Rectangle> clips = args.ProjToPixel(regions); DrawFeatures(args, drawIndices, clips, true); } else { // First determine the number of features we are talking about based on region. List<IFeature> drawList = new List<IFeature>(); foreach (IEnvelope region in regions) { if (region != null) { // We need to consider labels that go off the screen. figure a region // that is larger. IEnvelope r = region.Copy(); r.ExpandBy(region.Width, region.Height); // Use union to prevent duplicates. No sense in drawing more than we have to. drawList = drawList.Union(FeatureSet.Select(r)).ToList(); } } List<Rectangle> clipRects = args.ProjToPixel(regions); DrawFeatures(args, drawList, clipRects, true); } }
// This draws the individual line features private void DrawFeatures(MapArgs e, IEnumerable<IFeature> features) { Graphics g; if (e.Device != null) { g = e.Device; // A device on the MapArgs is optional, but overrides the normal buffering behaviors. } else { g = Graphics.FromImage(_backBuffer); } // Only draw features that are currently visible. IEnvelope drawExtents = e.PixelToProj(_drawingBounds); double minX = e.MinX; double maxY = e.MaxY; double dx = e.Dx; double dy = e.Dy; for (int selectState = 0; selectState < 2; selectState++) { foreach (ILineCategory category in Symbology.Categories) { // Define the symbology based on the category and selection state ILineSymbolizer ls = category.Symbolizer; if (selectState == SELECTED) ls = category.SelectionSymbolizer; if (ls.Smoothing) { g.SmoothingMode = SmoothingMode.AntiAlias; } else { g.SmoothingMode = SmoothingMode.None; } // Determine the subset of the specified features that are visible and match the category ILineCategory lineCategory = category; int i = selectState; Func<IDrawnState, bool> isMember = state => state.SchemeCategory == lineCategory && state.IsVisible && state.IsSelected == (i == 1); var drawnFeatures = from feature in features where isMember(DrawingFilter[feature]) select feature; GraphicsPath graphPath = new GraphicsPath(); foreach (IFeature f in drawnFeatures) { if (drawExtents.Contains(f.Envelope)) { // This optimization only works if we are zoomed out far enough // that the whole linestring fits in the short integer // drawing window. Otherwise, we need to crop the line. FastBuildLine(graphPath, DataSet.Vertex, f.ShapeIndex, minX, maxY, dx, dy); } else { for (int iPart = 0; iPart < f.NumGeometries; iPart++) { IBasicGeometry geom = f.GetBasicGeometryN(iPart); if (drawExtents.Contains(f.Envelope) == false) { geom = e.GeographicExtents.Intersection(geom); } // Cropped geometries can be either linestrings or multi-linestrings IBasicLineString bls = geom as IBasicLineString; if (bls != null) { // the result is definitely in the linestring category BuildLineString(graphPath, bls, minX, maxY, dx, dy); } else { IMultiLineString intersect = geom as MultiLineString; if (intersect == null) continue; for (int iLine = 0; iLine < intersect.NumGeometries; iLine++) { BuildLineString(graphPath, intersect.GetBasicGeometryN(iLine) as IBasicLineString, minX, maxY, dx, dy); } } } } } double scale = 1; if (ls.ScaleMode == ScaleModes.Geographic) { scale = e.ImageRectangle.Width / e.GeographicExtents.Width; } foreach (IStroke stroke in ls.Strokes) { stroke.DrawPath(g, graphPath, scale); } graphPath.Dispose(); } } if (e.Device == null) g.Dispose(); }
// This draws the individual line features private void DrawFeatures(MapArgs e, IEnumerable<int> features) { Graphics g = e.Device ?? Graphics.FromImage(_backBuffer); // Only draw features that are currently visible. if (FastDrawnStates == null) { CreateIndexedLabels(); } FastLabelDrawnState[] drawStates = FastDrawnStates; if (drawStates == null) return; //Sets the graphics objects smoothing modes g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; FeatureTypes type = FeatureSet.FeatureType; foreach (ILabelCategory category in Symbology.Categories) { List<int> catFeatures = new List<int>(); foreach (int fid in features) { if(drawStates[fid] == null || drawStates[fid].Category == null) continue; if (drawStates[fid].Category == category) { catFeatures.Add(fid); } } // Now that we are restricted to a certain category, we can look at // priority if (category.Symbolizer.PriorityField != "FID") { Feature.ComparisonField = category.Symbolizer.PriorityField; catFeatures.Sort(); // When preventing collisions, we want to do high priority first. // otherwise, do high priority last. if (category.Symbolizer.PreventCollisions) { if (!category.Symbolizer.PrioritizeLowValues) { catFeatures.Reverse(); } } else { if (category.Symbolizer.PrioritizeLowValues) { catFeatures.Reverse(); } } } foreach (int fid in catFeatures) { IFeature feature = FeatureSet.GetFeature(fid); switch (type) { case FeatureTypes.Polygon: DrawPolygonFeature(e, g, feature, drawStates[fid].Category, drawStates[fid].Selected, ExistingLabels); break; case FeatureTypes.Line: DrawLineFeature(e, g, feature, drawStates[fid].Category, drawStates[fid].Selected, ExistingLabels); break; case FeatureTypes.Point: DrawPointFeature(e, g, feature, drawStates[fid].Category, drawStates[fid].Selected, ExistingLabels); break; } } } if (e.Device == null) g.Dispose(); }
/// <summary> /// This draws to the back buffer. If the Backbuffer doesn't exist, this will create one. /// This will not flip the back buffer to the front. /// </summary> /// <param name="args"></param> /// <param name="regions"></param> /// <param name="clipRectangles"></param> private void DrawWindows(MapArgs args, IList<IEnvelope> regions, IList<Rectangle> clipRectangles) { Graphics g; if (args.Device != null) { g = args.Device; // A device on the MapArgs is optional, but overrides the normal buffering behaviors. } else { if (_backBuffer == null) _backBuffer = new Bitmap(_bufferRectangle.Width, _bufferRectangle.Height); g = Graphics.FromImage(_backBuffer); } int numBounds = Math.Min(regions.Count, clipRectangles.Count); for (int i = 0; i < numBounds; i++) { Bitmap bmp = _baseImage.GetBitmap(regions[i], clipRectangles[i]); if(bmp != null)g.DrawImage(bmp, clipRectangles[i]); } if (args.Device == null) g.Dispose(); }
// This draws the individual line features private void DrawFeatures(MapArgs e, IEnumerable<IFeature> features) { Graphics g = e.Device ?? Graphics.FromImage(_backBuffer); // Only draw features that are currently visible. if (DrawnStates == null || !DrawnStates.ContainsKey(features.First())) { CreateLabels(); } Dictionary<IFeature, LabelDrawState> drawStates = DrawnStates; if (drawStates == null) return; //Sets the graphics objects smoothing modes g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; FeatureTypes type = features.First().FeatureType; foreach (ILabelCategory category in Symbology.Categories) { var cat = category; // prevent access to unmodified closure problems //List<IFeature> catFeatures = (from feature in features // where drawStates.ContainsKey(feature) && drawStates[feature].Category == cat // select feature).ToList(); List<IFeature> catFeatures = new List<IFeature>(); foreach(IFeature f in features) { if(drawStates.ContainsKey(f)) { if(drawStates[f].Category == cat) { catFeatures.Add(f); } } } // Now that we are restricted to a certain category, we can look at // priority if(category.Symbolizer.PriorityField != "FID") { Feature.ComparisonField = cat.Symbolizer.PriorityField; catFeatures.Sort(); // When preventing collisions, we want to do high priority first. // otherwise, do high priority last. if(cat.Symbolizer.PreventCollisions) { if (!cat.Symbolizer.PrioritizeLowValues) { catFeatures.Reverse(); } } else { if (cat.Symbolizer.PrioritizeLowValues) { catFeatures.Reverse(); } } } foreach (IFeature feature in catFeatures) { switch (type) { case FeatureTypes.Polygon: DrawPolygonFeature(e, g, feature, drawStates[feature].Category, drawStates[feature].Selected, ExistingLabels); break; case FeatureTypes.Line: DrawLineFeature(e, g, feature, drawStates[feature].Category, drawStates[feature].Selected, ExistingLabels); break; case FeatureTypes.Point: DrawPointFeature(e, g, feature, drawStates[feature].Category, drawStates[feature].Selected, ExistingLabels); break; } } } if (e.Device == null) g.Dispose(); }
/// <summary> /// If useChunks is true, then this method /// </summary> /// <param name="args">The GeoArgs that control how these features should be drawn.</param> /// <param name="features">The features that should be drawn.</param> /// <param name="clipRectangles">If an entire chunk is drawn and an update is specified, this clarifies the changed rectangles.</param> /// <param name="useChunks">Boolean, if true, this will refresh the buffer in chunks.</param> public virtual void DrawFeatures(MapArgs args, List<IFeature> features, List<Rectangle> clipRectangles, bool useChunks) { if (useChunks == false || features.Count < ChunkSize) { DrawFeatures(args, features); return; } int count = features.Count; int numChunks = (int)Math.Ceiling(count / (double)ChunkSize); for (int chunk = 0; chunk < numChunks; chunk++) { int groupSize = ChunkSize; if(chunk == numChunks-1) groupSize = count - chunk*ChunkSize; List<IFeature> subset = features.GetRange(chunk*ChunkSize, groupSize); DrawFeatures(args, subset); if (numChunks <= 0 || chunk >= numChunks - 1) continue; FinishDrawing(); OnBufferChanged(clipRectangles); System.Windows.Forms.Application.DoEvents(); } }
/// <summary> /// Draws a label on a polygon with various different methods /// </summary> /// <param name="e"></param> /// <param name="g"></param> /// <param name="f"></param> /// <param name="category"></param> /// <param name="selected"></param> /// <param name="existingLabels"></param> private static void DrawPolygonFeature(MapArgs e, Graphics g, IFeature f, ILabelCategory category, bool selected, List<RectangleF> existingLabels) { ILabelSymbolizer symb = category.Symbolizer; if (selected) symb = category.SelectionSymbolizer; //Gets the features text and calculate the label size string txt = GetLabelText(f, category); if (txt == null) return; SizeF labelSize = g.MeasureString(txt, symb.GetFont()); if(f.NumGeometries == 1) { RectangleF labelBounds = PlacePolygonLabel(f.BasicGeometry, e, labelSize, symb); CollisionDraw(txt, g, symb, e, labelBounds, existingLabels); } else { if (symb.LabelParts == LabelParts.LabelAllParts) { for (int n = 0; n < f.NumGeometries; n++) { RectangleF labelBounds = PlacePolygonLabel(f.GetBasicGeometryN(n), e, labelSize, symb); CollisionDraw(txt, g, symb, e, labelBounds, existingLabels); } } else { double largestArea = 0; IPolygon largest = null; for (int n = 0; n < f.NumGeometries; n++) { IPolygon pg = Geometry.FromBasicGeometry(f.GetBasicGeometryN(n)) as IPolygon; if (pg == null) continue; double tempArea = pg.Area; if (largestArea < tempArea) { largestArea = tempArea; largest = pg; } } RectangleF labelBounds = PlacePolygonLabel(largest, e, labelSize, symb); CollisionDraw(txt, g, symb, e, labelBounds, existingLabels); } } //Depending on the labeling strategy we do diff things }
/// <summary> /// This will draw any features that intersect this region. To specify the features /// directly, use OnDrawFeatures. This will not clear existing buffer content. /// For that call Initialize instead. /// </summary> /// <param name="args">A GeoArgs clarifying the transformation from geographic to image space</param> /// <param name="regions">The geographic regions to draw</param> /// <returns>The list of rectangular areas that match the specified regions</returns> public void DrawRegions(MapArgs args, List<IEnvelope> regions) { // First determine the number of features we are talking about based on region. List<Rectangle> clipRects = args.ProjToPixel(regions); if (EditMode) { List<IFeature> drawList = new List<IFeature>(); foreach (IEnvelope region in regions) { if (region != null) { // Use union to prevent duplicates. No sense in drawing more than we have to. drawList = drawList.Union(DataSet.Select(region)).ToList(); } } DrawFeatures(args, drawList, clipRects, true); } else { List<int> drawList = new List<int>(); List<Extent> extents = new List<Extent>(); double[] verts = DataSet.Vertex; foreach (IEnvelope envelope in regions) { extents.Add(new Extent(envelope)); } if (DataSet.FeatureType == FeatureTypes.Point) { for(int shp = 0; shp < verts.Length/2; shp++) { foreach (Extent extent in extents) { if(extent.Intersects(verts[shp * 2], verts[shp * 2+ 1])) { drawList.Add(shp); } } } } else { List<ShapeRange> shapes = DataSet.ShapeIndices; for (int shp = 0; shp < shapes.Count; shp++) { foreach (IEnvelope region in regions) { if (!shapes[shp].Extent.Intersects(region)) continue; drawList.Add(shp); break; } } } DrawFeatures(args, drawList, clipRects, true); } }
private static void CollisionDraw(string txt, Graphics g, ILabelSymbolizer symb, MapArgs e, RectangleF labelBounds, List<RectangleF> existingLabels) { if (labelBounds == RectangleF.Empty || !e.ImageRectangle.IntersectsWith(labelBounds)) return; if(symb.PreventCollisions) { if(!Collides(labelBounds, existingLabels)) { DrawLabel(g, txt, labelBounds, symb); existingLabels.Add(labelBounds); } } else { DrawLabel(g, txt, labelBounds, symb); } }
// This draws the individual line features private void DrawFeatures(MapArgs e, IEnumerable<IFeature> features) { Graphics g = e.Device ?? Graphics.FromImage(_backBuffer); double minX = e.MinX; double maxY = e.MaxY; double dx = e.Dx; double dy = e.Dy; IDictionary<IFeature, IDrawnState> states = DrawingFilter.DrawnStates; if (states == null) return; foreach (IPointCategory category in Symbology.Categories) { foreach (IFeature feature in features) { if (states.ContainsKey(feature) == false) continue; IDrawnState ds = states[feature]; if (ds == null) continue; if (!ds.IsVisible) continue; if (ds.SchemeCategory == null) continue; IPointCategory pc = ds.SchemeCategory as IPointCategory; if (pc == null) continue; if (pc != category) continue; IPointSymbolizer ps = pc.Symbolizer; if (ds.IsSelected) { ps = pc.SelectionSymbolizer; } if (ps == null) continue; g.SmoothingMode = ps.Smoothing ? SmoothingMode.AntiAlias : SmoothingMode.None; double[] vertices = feature.ParentFeatureSet.Vertex; // necessary to trigger vertex creation ShapeRange range = feature.ShapeIndex; for (int iPart = 0; iPart < range.Parts.Count; iPart++ ) { PartRange prt = range.Parts[iPart]; vertices = prt.Vertices; for (int i = prt.StartIndex; i <= prt.EndIndex; i++) { System.Drawing.Point pt = new System.Drawing.Point(); pt.X = Convert.ToInt32((vertices[i * 2] - minX) * dx); pt.Y = Convert.ToInt32((maxY - vertices[i * 2 + 1]) * dy); double scaleSize = 1; if (ps.ScaleMode == ScaleModes.Geographic) { scaleSize = e.ImageRectangle.Width / e.GeographicExtents.Width; } Matrix old = g.Transform; Matrix shift = g.Transform; shift.Translate(pt.X, pt.Y); g.Transform = shift; ps.Draw(g, scaleSize); g.Transform = old; } } } } if(e.Device == null)g.Dispose(); }
/// <summary> /// This draws to the back buffer. If the Backbuffer doesn't exist, this will create one. /// This will not flip the back buffer to the front. /// </summary> /// <param name="args"></param> /// <param name="regions"></param> /// <param name="clipRectangles"></param> private void DrawWindows(MapArgs args, IList<IEnvelope> regions, IList<Rectangle> clipRectangles) { Graphics g = null; if (args.Device != null) { g = args.Device; // A device on the MapArgs is optional, but overrides the normal buffering behaviors. } else { if (_backBuffer == null) _backBuffer = new Bitmap(_bufferRectangle.Width, _bufferRectangle.Height); g = Graphics.FromImage(_backBuffer); } int numBounds = Math.Min(regions.Count, clipRectangles.Count); int minX = args.ImageRectangle.X; int minY = args.ImageRectangle.Y; int maxX = args.ImageRectangle.X + args.ImageRectangle.Width; int maxY = args.ImageRectangle.Y + args.ImageRectangle.Height; for(int i = 0; i< numBounds; i++) { // For panning tiles, the region needs to be expanded. // This is not always 1 pixel. When very zoomed in, this could be many pixels, // but should correspond to 1 pixel in the source image. int dx = (int)Math.Ceiling(DataSet.Bounds.AffineCoefficients[1]*clipRectangles[i].Width/regions[i].Width); Rectangle r = clipRectangles[i].ExpandBy(dx*2); if (r.X < 0) r.X = 0; if (r.Y < 0) r.Y = 0; IEnvelope env = regions[i].Reproportion(clipRectangles[i], r); Bitmap bmp = _baseImage.GetBitmap(env, r); if (bmp == null) continue; g.DrawImage(bmp, r); bmp.Dispose(); } if (args.Device == null) g.Dispose(); }