georeference.org
Subscribe to this thread
Home - General / All posts - mulitple labels on 1 point
earthmoon5 post(s)
#26-Jan-08 14:49

Hi all - I'm working on a project geocoding tens of thousands of points. Each point can have as many as 20 names associated with it, and I cannot for the life of me figure out how to set up the labels so that they are all visible. I've fiddled endlessly with "display options" and rotations and rotation themes and nothing gives me a pattern that can be read. I need to see these labels either all in a stack/list or in a radial pattern around the point, just so that they are all (mostly) readable. Since I'm doing so many points, hand positioning the labels is just not feasible.

Any help is very welcome!

Dimitri


3,140 post(s)
#26-Jan-08 15:43

Easy, depending on what it is you mean by "point." A "point" is a specific type of object in Manifold but I don't know if you are using this word precisely or if you are using it less precisely as a synonym to mean location, a particular place, etc. How you approach the matter depends on what you mean.

Case 1: "Point" means point object in Manifold.

In this case, what do you mean by Each point can have as many as 20 names associated with it ? If by that you mean that each point can have many data attributes giving possible names, then the solution is simple. Create a labels component and in the Text box cite each of the attribute fields that can be used for names so that each name appears in the same label on a different line, just like the example in the Creating Labels from Fields help topic.

Another way of doing this is to use the transform toolbar to concatenate all the different fields that might hold names into a single field and then use that field in the label. This would put all the names for the same point on one line in the label.

Case 2: "Point" means the same location, such as a city.

I'm guessing here, but I suppose what you mean is that after you geocode your data set you end up with a bunch of point objects at the same location and that each of those has a different name. I'm guessing that when you create labels for those different point objects using each point's name field you end up with a bunch of labels that overlap each other.

This is trickier but still can be done and you probably won't even have to use SQL. How you go about doing it depends on the structure of your data. For example, are all of the point objects representing the same city located at identically the same lat/lon coordinates? Or, is it because the city data you are using arises from several different sources that the point objects you end up with are approximately in the same location for each city but are not exactly coincident? Begin by making backups of all starting data. :-)

Suppose it is the former, that all the different points for a city exactly coincide. Open up a table and turn on the lat/lon (I) intrinsic fields. Select all duplicates except the first using, say, Longitude (I) [assuming no longitude is identically the same for different cities, a safe bet if the data is at all precise] and then cut them and save them to a new drawing. You now have a drawing that has only one point in each city with a name field, say, called Name, and you also have a drawing with all the duplicates.

In the drawing with unduplicated points create a buffer zone area around each point [choosing a distance such as 1000 meters that will capture all points for the same city but won't overlap with any other buffer zone], transferring the values of all fields into that buffer zone. This area will be a convenient scratchpad used to transfer values.

In the drawing of duplicates change the Name field to Name2. Again, select all duplicates except the first for Longitude (I) in that drawing and now Invert the selection. Cut those and paste them back into the first drawing. You now have the original drawing that had no duplicated points, but you've also added one duplicate to some of those points where the duplicate uses a field name of Name2.

Transfer the Name2 values from those points with a value for Name2 into the buffer zone area objects that contain them. Now, delete those points with a value in Name 2. Next, transfer the Name2 values from the buffer zone area objects into the Name2 field (up to now, empty) for the points contained by each buffer zone.

Use the transform toolbar to concatenate the names from the Name2 field into Name and after doing that clear the values in Name2 for both points and buffer zone objects. So now some of the points have a longer string in Name that consists of two names.

Go back to your drawing that has the duplicates. Once again select all duplicates except the first on Longitude (I) and cut those. Paste them back into the first drawing. Now you've added a bunch more new, coincident points with a value in Name2. Once again transfer those Name2 values into the containing buffer zone areas and then delete those points with values in Name2. Once again transfer the Name2 values from the buffer zone areas into those points contained by each area. Concatenate those onto Name and delete the Name2 values from points and areas. You now have a drawing of points where some of those points have three names in their Name string.

Repeat that process until you've run out of duplicated points in that second drawing, and then you are done. You can now create a label for each point and it will list all the names in that Name field.

Note that the above process works no matter how many points you have. If you have 1,000,000 points you don't have to do it 1,000,000 times: you only have to do it to whatever "depth" of duplication you might have. That is, if you can have up to 20 points located at the same spot representing the same city you only have to do it 20 times, as the sifting works regardless of how many particular cities are duplicated or how many cities there are.

If you have a situation where the points representing a city are near each other but not exactly coincident, you can't use the simple trick of using the transform toolbar to select all duplicates except the first using the Longitude (I) field. That's a bit trickier. You being the same way to assure yourself you have a set of points where none are exactly coincident, and then you create a small buffer zone about each and then you use that buffer zone to select all that are contained within that buffer zone, giving each object thus selected some attribute value that marks it as a member of that same city. You can then do the all duplicates but first trick using that attribute value.

A caution: the above is a thought experiment, not a report of each step actual done in real life. But it shows one possible approach. Another approach would be to accomplish the above through elegant SQL; however, writing the exact SQL depends on the exact structure of your data.

earthmoon5 post(s)
#26-Jan-08 22:21

Argh - sorry I wasn't nearly specific enough. I shall blame the hours and hours I beat my head on this particular problem :D I've been switching from Arc just fine, but this is one of those "I should know how to do this" problems and it has eaten my brain that I can't use a familiar tool. Okay, inline comments below....

Easy, depending on what it is you mean by "point."

An actual point - a specific lon/lat location. I'm geocoding residential addresses and often several people reside in the same parcel - meaning that, say, an apartment building may have 20 people living there and I need to set up the labels to display those 20 names in a list or at least radially around the point.

If by that you mean that each point can have many data attributes giving possible names, then the solution is simple. Create a labels component and in the Text box cite each of the attribute fields that can be used for names so that each name appears in the same label on a different line, just like the example in the Creating Labels from Fields help topic.

Alas - it's multiple labels from one field.

Suppose it is the former, that all the different points for a city exactly coincide. Open up a table and turn on the lat/lon (I) intrinsic fields. Select all duplicates except the first using, say, Longitude (I) [assuming no longitude is identically the same for different cities, a safe bet if the data is at all precise] and then cut them and save them to a new drawing. You now have a drawing that has only one point in each city with a name field, say, called Name, and you also have a drawing with all the duplicates.

In the drawing with unduplicated points create a buffer zone area around each point [choosing a distance such as 1000 meters that will capture all points for the same city but won't overlap with any other buffer zone], transferring the values of all fields into that buffer zone.

If I'm understanding your description properly, I can't apply it to this particular map. Since each point I'm trying to label is the center point of a residential parcel, buffering would have to be a matter of inches or feet and wouldn't be very effective that way since the label text itself is "feet" tall with respect to the map dimensions :) I feel like I'm doing surgery on a wee wee mouse trying to manipulate labels in this small space. In Arc I would use Maplex to arrange the labels 360 degrees around the point (all at the same rotation so they were easily readable). It wasn't perfect, but most of the time you could read all the labels. Only when I had an apartment building with 30 residents did the overlapping problems recur. Manifold puts Arc in the dust with all other aspects of this map project, I'm sure Manifold can do this better too. If I can figure it out :)

Another approach would be to accomplish the above through elegant SQL; however, writing the exact SQL depends on the exact structure of your data.

I figured it might come to that - I know exactly no SQL. In the help files there's mention of a script to arrange multiple labels, but I don't even know where to begin with that. You've been so helpful thus far - any new direction to point me in?

tjhb

3,200 post(s)
#26-Jan-08 22:38

Would it be right to say that you have multiple points, many of which are exactly coincident; and that for each group of coincident points (an apartment block) you want to create a single label, or a ring of labels, made by combining the name fields for all of the points in the group?

If this is about right, can you give the drawing name and the name of the name field?

earthmoon5 post(s)
#27-Jan-08 19:40

Yup, that's the set up.

drawing name: walklist

field: lastname

Thanks in advance for the help!

adamw


4,672 post(s)
#28-Jan-08 00:15

Here is how to aggregate the text from individual objects together:

Create a new drawing with a point at each location by importing the following query as a drawing:

--SQL

OPTIONS CoordSys("walklist" AS COMPONENT);

SELECT First([geom (i)]) g, First(lastname) lastname

FROM walklist GROUP BY [x (i)][y (i)]

Name the resulting drawing "points".

Create the following query which would scan the original drawing and return some of the objects whose names have not yet been merged into the new drawing, name that query "pointsget":

--SQL

SELECT t.id, First(s.lastname) namenew

FROM points t INNER JOIN walklist s ON

  s.[x (i)] = t.[x (i)] AND

  s.[y (i)] = t.[y (i)] AND

  Position(

    Chr(10) & Chr(13) & s.lastname & Chr(10) & Chr(13) IN

    Chr(10) & Chr(13) & t.lastname & Chr(10) & Chr(13)

  ) = 0

GROUP BY t.id

Create one more query which would run the "pointsget" query and merge the names of the objects in the original drawing it returns into the new drawing, name the query "pointsupd":

--SQL

UPDATE (

  SELECT u.lastname, v.namenew

  FROM points u INNER JOIN pointsget v ON u.id=v.id

)

SET u.lastname = u.lastname & Chr(10) & Chr(13) & v.namenew

Run the "pointsupd" query until the "pointsget" query returns no records. There is no harm if you run the "pointsupd" query a few times more than is necessary.

Now, add a labels component bound to the "points" drawing and use the "lastname" field for label text.

KlausDE

4,178 post(s)
#27-Jan-08 00:55

Some SQL along the lines of this thread aggregating labels contained in a circle. I'm curious if Tim or someone else finds a solution for a more appealing positioning of multiple labels than just a list. I think of google earth lables of coincident fotos for instance.

A long lasting feature request of mine is a way to have the actual position of the lable text as an additional geom published in the object model. In effect you should be able to control both ends of a callout. As I love SQL my feature request included to get a virtual table for label components with those both geoms, text and formating acessible for SQL and scripts.

Dimitri


3,140 post(s)
#27-Jan-08 10:53

Since each point I'm trying to label is the center point of a residential parcel, buffering would have to be a matter of inches or feet and wouldn't be very effective that way since the label text itself is "feet" tall with respect to the map dimensions :) I feel like I'm doing surgery on a wee wee mouse trying to manipulate labels in this small space.

I think you might want to re-read how labels are created from fields, as there is no issue with buffer sizes and label sizes or having to avoid selecting labels when doing selection on objects.

Your situation is "Case 2" as I described it, and what I suggested will work. I think you are getting confused by the final step of actually creating a label. Don't focus on that. Your first task is to assemble data from multiple, coincident point objects into a single point object at that location. You can then create a single label from that single point object. Because it is a single label at that location it doesn't conflict with other labels at that location. The only question is how you prefer to represent multiple names in that single label. For example, you could put multiple names onto a single line, each separated by commas or whatever, or you could show multiple names in a list on multiple lines.

The first step is massaging the data into convenient form. Your objective is to concantenate the "Name" fields from multiple point objects into a single "Name" field. Then you can automatically create a label from that. Alternatively, you can move the data from a single Name field that occurs in multiple, coincident point objects into multiple Name fields within a single object and then create a label from that (which gives you the flexibility of using a list or more complex format for the label).

For example, if you have points A, B and C and each has a Name field in it that contain, respectively, "John", "Paul" and "George" and all three points are coincident, then you can't automatically create a label from the Name field that won't have the three labels created from three coincident point objects colliding. But what you can do is concatenate information so that a single point object has the information from all three objects and then you can create a single label that contains all the information. So you can concatenate info from all three coincident points so that, say, the A point object now has a Name field that contains "John, Paul, George" and then create a single label that has that as a single line. If you prefer a list, you could use a similar technique to populate the A object with three fields, Name1, Name2 and Name3 and create a label using all three fields at once so you could have a label like

"John

Paul

George"

with the information on separate lines.

The trick is not in creating the label itself (easy) it is how to automatically identify all the points that are coincident and how to transfer the information between them to do the concatenation so that all the information desired is within a single point object's field(s). The Case 2 procedure I give you does that. Creating the label itself after that takes no time at all.

[If you haven't done so yet, take the time to review the topics in the Introduction on selections and the topics in drawings using the transform toolbar. Review the Example topics showing how to use tokens in the transform toolbar as well so you know what I mean by "concatenate". Also, read carefully the Labels topics including the one I cited earlier so you know how to automatically create labels from fields.]

By the way, the Case 2 procedure doesn't take long to do. Once you learn how to use the transform toolbar it will take about ten minutes to actually do.

I don't know what the ultimate application is, but there are more sophisticated ways of displaying complex data that are used in, say, web applications. For example, if you really can have 20 or 100 different data sets associated with the same point and you want people to be able to browse that in a web application then the way to do it is to use two different point icons, one for single records at that location and another one for multiple records at that location. The multiple records could utilize a URL field so that when someone double-clicks on that point it opens a browser that enables people to scroll through the linked DBMS records referenced by that record.

mikedufty

659 post(s)
#28-Jan-08 21:56

I'm curious about the "easy" bit, displaying the label. How can you generate a label that has a list of between 1 and 20 or 30 names, without it creating giant blank labels for the instances with only one name?

I haven't tried it recently, but I recall trying something similar to generate multiline labels by splitting a text column into two, and putting the two columns into a label separated by a line break. It sort of worked, but messed up the formatting, particularly the vertical alignment for any label requiring only one line of text (second column blank).

adamw


4,672 post(s)
#28-Jan-08 23:16

You can avoid blank lines by embedding line breaks into field values (create a line break using an active column: Chr(13) & Chr(10), then append it to non-empty text values in the involved fields using the Transform toolbar) and setting label text to: [Field 1][Field 2][Field 3], without any text between the field references.

mikedufty

659 post(s)
#29-Jan-08 00:34

Excellent, I have previously only been able to get line breaks in by importing from excel.

mikedufty

659 post(s)
#07-Feb-08 23:32

just tried using active column

Function Func

Func = Chr(13)&Chr(10) ' Record.Data("Column")

End Function

And it doesn't seem to work for me. I suspect there is something very obvious I have missed.

I am using ansi variable length for the column types.

I can append the active column result, but it doesn't seem to do anything to labels.

KlausDE

4,178 post(s)
#07-Feb-08 23:43

Your Func() returns a vbNewLine only. The value of [Column] is not concatenated and commented out.

' VbScript

Function Func

   Func = Record.Data("Column") & Chr(13) & Chr(10)

End Function

But this will not solve your black lines problem. To get what Adam assumed you have to select all records where [Column] is not "" and with the tables Transform toolbar manually add Chr(13) & Chr(10) to all selected records ONLY. You don't need an active column then.

adamw


4,672 post(s)
#08-Feb-08 07:06

Or, alternatively, do the is-empty check within the active column itself:

'VBScript

Function Func

  s = Record.Data("Column")

  If s = "" Then

    Func = s

  Else

    Func = s & Chr(13) & Chr(10)

  End If

End Function

mikedufty

659 post(s)
#10-Feb-08 15:52

How do you manually add Chr(13)&Chr(10) with the transform toolbar? This is what I would really like to do. Using append just puts that in as text. Do I need the column to be a non-text type?

I have tried Adam's script to do everything in the active column, and that is working great, thankyou, but I would like to have a simpler option for just adding the occasional multiline label manually if it is possible.

adamw


4,672 post(s)
#13-Feb-08 09:34

Open the table. Create a new text column. Open Notepad. Press Enter to create a new line. Press Ctrl-A to select all text, then Ctrl-C to copy the text into the Clipboard. Switch to Manifold. Select all records. Right-click one of the cells in the new column and do Paste Append. Click into the cell, then press Enter to propagate the pasted value to all selected cells (requires the "Apply table cell editing to all selected records" option in Tools - Options, User Interface to be turned on). You can now combine the values in the new column with the values in other columns using the Transform toolbar.

mikedufty

659 post(s)
#13-Feb-08 17:05

Great, thanks, I tried that and it works for me.

Also works for legend entries derived from the column text, another thing I've wished I could do.

Would be nicer still if you could do something like alt-enter to get the same effect as in excel.

0 msec Copyright (C) 2007-2008 Manifold.net. All rights reserved.