MapInfo Pro

 View Only

MapInfo Monday: Offset Labels to the left and right 2

  • 1.  MapInfo Monday: Offset Labels to the left and right 2

    Employee
    Posted yesterday

    This is a follow-up article to last week's post of offsetting labels to the left and to the right.

    I promised to give you some hints on assigning values in a more automatic way. You can find that in today's article.

    Happy #MapInfoMonday!

    Assigning Initial Values to the Points

    As I said in last week's post, the initial task of assigning values to the points could probably be automated - at least to a certain degree.

    Let's look at how you can do this.

    For a given dataset, you can use MapBasic to determine the coverage of spatial data. This wil be returned as the minimum and maximum coordinates of the spatial objects.

    Set CoordSys Table sTab
    
    fMinLong = TableInfo(sTab, TAB_INFO_MINX)
    fMaxLong = TableInfo(sTab, TAB_INFO_MAXX)
    fMinLat = TableInfo(sTab, TAB_INFO_MINY)
    fMaxLat = TableInfo(sTab, TAB_INFO_MAXY)

    When you know the coverage, you can start calculating values to the East and West of the dataset. And you can calculate a middle of the dataset to help you split the dataset into two. These two parts will probably not be of equal size, as that would assume the data is evenly distributed - which it rarely is.

    The value fCentLong can help me select points to the left and right of the center of the dataset.

    fCentLong = fMaxLong - ((fMaxLong - fMinLong) / 2)
    To make it easier to work with the values, I'll round the initial values to limit the number of decimals.
    fMinLong = Round(fMinLong, 0.01)
    fMaxLong = Round(fMaxLong, 0.01)
    fMaxLat = Round(fMaxLat, 0.01)

    I can now select the first part of my data, the Western points and calculate value for these.

    I select all the records where the CentroidX() of the object is less than or equal to the center value, fCentLong.

    I order the result by the Y coordinate so that the labels would appear in this order too.

    Select h.LABEL_LONG, h.LABEL_LAT, h.LABEL_POS, CentroidY(h.OBJ) As "LAT"
    From sTab As "h"
    Where CentroidX(h.OBJ) <= fCentLong
    Order By LAT Desc
    Into _west NoSelect Hide
    
    Update _west
    Set LABEL_POS = "W",
      LABEL_LONG = fMinLong - 0.05,
      LABEL_LAT = fMaxLat - ((ROWID - 1) * 0.05)
    Close Table _west

    I update the resulting query and set the LABEL_POS to "w", and set the X coordinate of the label point to be placed west of the current data bounds, and finally I calculate the Y coordinate of the labels from the maximum Y coordinate of the data bounds to a lower Y coordinate based on this simple expression:

    fMaxLat - ((ROWID - 1) * 0.05)
    The first label point will be placed at the same latitude as the data bounds. The next one will be placed 0.05 degrees below the highest data bound and so on placing each label points 0.05 degrees lower than the previous.

    This value, 0.05, is dependent on your map scale. So you may have to do a bit of trial and error to find the right value.

    I do similar calculations for the points that will be moved to the East.

    Select h.LABEL_LONG, h.LABEL_LAT, h.LABEL_POS, CentroidY(h.OBJ) As "LAT"
    From sTab As "h"
    Where CentroidX(h.OBJ) > fCentLong
    Order By LAT Desc
    Into _east NoSelect Hide
    
    Update _east
    Set LABEL_POS = "E",
      LABEL_LONG = fMaxLong + 0.05,
      LABEL_LAT = fMaxLat - ((ROWID - 1) * 0.05)
    Close Table _east

    After this, I can reuse the script from last week's post to create the label point and label line layers. The new elements of the script look like this:

    image
    The result looks like this in a map
    image

    As you can quickly see, this is not the final result. 

    You will need to change the latitude for some of the labels to avoid the overlapping label lines.

    You could also consider moving some of the labels from the East to the West to better spread the labels on the two sides.

    In case you are interested in the full script, it can be found here:
    Dim sTab As String
    Dim fMinLong As Float
    Dim fMaxLong As Float
    Dim fMinLat As Float
    Dim fMaxLat As Float
    Dim fCentLong As Float
    
    sTab = "Hospitals"
    Set CoordSys Table sTab
    
    fMinLong = TableInfo(sTab, TAB_INFO_MINX)
    fMaxLong = TableInfo(sTab, TAB_INFO_MAXX)
    fMinLat = TableInfo(sTab, TAB_INFO_MINY)
    fMaxLat = TableInfo(sTab, TAB_INFO_MAXY)
    fCentLong = fMaxLong - ((fMaxLong - fMinLong) / 2)
    
    fMinLong = Round(fMinLong, 0.01)
    fMaxLong = Round(fMaxLong, 0.01)
    fMaxLat = Round(fMaxLat, 0.01)
     
    Select h.LABEL_LONG, h.LABEL_LAT, h.LABEL_POS, CentroidY(h.OBJ) As "LAT"
    From sTab As "h"
    Where CentroidX(h.OBJ) <= fCentLong
    Order By LAT Desc
    Into _west NoSelect Hide
    
    Update _west
    Set LABEL_POS = "W",
      LABEL_LONG = fMinLong - 0.05,
      LABEL_LAT = fMaxLat - ((ROWID - 1) * 0.05)
    Close Table _west
    
    Select h.LABEL_LONG, h.LABEL_LAT, h.LABEL_POS, CentroidY(h.OBJ) As "LAT"
    From sTab As "h"
    Where CentroidX(h.OBJ) > fCentLong
    Order By LAT Desc
    Into _east NoSelect Hide
    
    Update _east
    Set LABEL_POS = "E",
      LABEL_LONG = fMaxLong + 0.05,
      LABEL_LAT = fMaxLat - ((ROWID - 1) * 0.05)
    Close Table _east
    
    Select h.TRADE_NAME, h.*
    , CreatePoint(h.LABEL_LONG, h.LABEL_LAT) object
    From sTab As "h"
    Into _lbl_pnts_all NoSelect
    
    Select * From _lbl_pnts_all
    Where LABEL_POS = "W"
    Into _lbl_pnts_w NoSelect
    Select * From _lbl_pnts_all
    Where LABEL_POS = "E"
    Into _lbl_pnts_e NoSelect
    
    Add Map Auto Layer _lbl_pnts_w, _lbl_pnts_e
    Set Map Layer _lbl_pnts_w Display Global
       Global Symbol (32,16744448,12,"MapInfo Symbols",256,0)
    Set Map Layer  _lbl_pnts_e Display Global
       Global Symbol (32,16744448,12,"MapInfo Symbols",256,0)
    
    Select h.*
    , CreateLine(CentroidX(h.obj), CentroidY(h.Obj), h.LABEL_LONG, h.LABEL_LAT) object
    From sTab As "h"
    Into _lbl_lns_all NoSelect
    
    Select * From _lbl_lns_all
    Where LABEL_POS = "W"
    Into _lbl_lns_w NoSelect
    
    Select * From _lbl_lns_all
    Where LABEL_POS = "E"
    Into _lbl_lns_e NoSelect
    
    Add Map Auto Layer _lbl_lns_w, _lbl_lns_e
    Set Map Layer _lbl_lns_w Display Global Global Line (1,2,16711680)
    Set Map Layer _lbl_lns_e Display Global Global Line (1,2,16711680)


    ------------------------------
    Peter Horsbøll Møller
    Principal Presales Consultant | Distinguished Engineer
    Precisely | Trust in Data
    ------------------------------