Aligning labels.

Jul 8, 2011 at 7:17 PM


Hello everyone.

I've seen in previous messages that seems to be possible alignment of labels from one of the angles of the shp lines? Any other possibility to perform this alignment? (line by line).

Thanks

Jul 20, 2011 at 7:24 PM

I was faced with the same problem. I was trying to add street labels between blocks a la Google Maps. I'd seen a discussion in which it was recommended to use a transparent point layer containing label text and angle attributes. I converted your annotation class to a point layer using the following ArcScript:

http://edndoc.esri.com/arcobjects/9.0/Samples/Geodatabase/Creating_and_Converting_Data/Convert_an_annotation_class_to_a_feature_class/Convert_an_annotation_class_to_a_feature_class.htm

and added some code like this to the maps ViewExtentsChanged event handler:

IMapFeatureLayer mfl=map.Layers[<Your Layer Index>] as IMapFeatureLayer;

using (Graphics g = Graphics.FromImage(map.BufferedImage))
                {
                    MapArgs args = new MapArgs(map.ClientRectangle, map.ViewExtents, g);
                    using (Brush brush = new SolidBrush(Color.Black))
                    {
                        float currentSize = Convert.ToSingle(args.ProjToPixel(30));
                        using (Font font = new Font("Arial", currentSize, FontStyle.Bold, GraphicsUnit.Pixel))
                        {
                            foreach (IFeature feature in mflPoint.DataSet.Features)
                            {
                                string label = feature.DataRow["LabelText"].ToString();
                                double dAngle = Convert.ToDouble(feature.DataRow["Angle"].ToString());
                                GraphicsPath gp = new GraphicsPath();
                                Coordinate c = feature.Coordinates[0];
                                float x = Convert.ToSingle((c.X - args.MinX) * args.Dx);
                                float y = Convert.ToSingle((args.MaxY - c.Y) * args.Dy);
                                PointF point = new PointF(x, y);

                                gp.AddString(label, font.FontFamily, (int)font.Style, (int)font.Size,
                                    point, StringFormat.GenericDefault);
                                if (dAngle != 0)
                                {
                                    Matrix matrix = new Matrix();
                                    matrix.RotateAt((float)dAngle, point);
                                    gp.Transform(matrix);
                                }
                                g.FillPath(brush, gp);
                            }

                       }

                  }

           }

The problem with the ArcScript is that the point is placed at the lower left corner of the annotation feature's envelope. This is a problem because the System.Drawing.Graphics class will draw the label from the upper left. The way I dealt with this problem was to write a script similar to the following:

http://edndoc.esri.com/arcobjects/9.0/Samples/Editing/MoveFeatures.htm

to move the points belonging to annotations whose angle was 0 or 90 degrees down (x + height of the annotation) or left (y + height of the annotation)(respectively). Points with other angle attributes I edited manually.

If you only require straight labeling. The above solution should work. However, I had a few labels that would have looked hideous if they were rendered straight so I needed to find a way to draw them along a path. I found the following code which does this:

http://www.guidebee.biz/forum/viewthread.php?tid=84

You could use this for all your labels but it's very slow if you have a lot of labels to render. Fortunately, in my case, I only had about 45 or so labels that I needed to use this code for. I isolated those annotation features and selected the center line segments that intersected them and copied them into a new poly line shape file. I trimmed the line segments to the length of the annotations. I was then able to add the poly line shape file to the map control. I gave it a transparent LineSymbolizer and added the following code to my ViewExtentsChanged event handler:

IMapFeatureLayer mflLine=map.Layers[<Your Layer Index>] as IMapFeatureLayer;

foreach (IFeature feature in mflLine.DataSet.Features)
                            {
                                string label = feature.DataRow["ROAD_NAME"].ToString();
                                int numCoords = feature.Coordinates.Count;
                                System.Drawing.Point[] points = new System.Drawing.Point[numCoords];
                                for (int i = 0; i < numCoords; i++)
                                {
                                    Coordinate c = feature.Coordinates[i];
                                    float x = Convert.ToSingle((c.X - args.MinX) * args.Dx);
                                    float y = Convert.ToSingle((args.MaxY - c.Y) * args.Dy);
                                    points[i] = new System.Drawing.Point((int)x, (int)y);
                                }
                                GraphicsPath gp = new GraphicsPath();
                                gp.AddLines(points);
                                if (gp.PathPoints[0].X > gp.PathPoints[gp.PointCount - 1].X)
                                    gp.Reverse();
                                g.DrawString(label, font, brush, TextPathAlign.Center,
                                        TextPathPosition.CenterPath, gp);
                            }

This worked for me. Performance is pretty good too.