Android Question OSMdroid Mapview and Text overlays

Discussion in 'Android Questions' started by Kevin Hartin, Apr 3, 2019.

  1. Kevin Hartin

    Kevin Hartin Member Licensed User

    I am close to releasing my Tour Samoa app and would like to overlay village names on my Map. This is necessary for my search capability, but also for ease of navigation for tourists.

    I currently have about 6 types of Points Of Interest each with a different icon, which is great, but none of the maps I have available has a comprehensive list of villages in their tiles. What I would like to do is place them like markers, but instead of icons, have a small text marker instead. I would have to enable them in groups based on zoom levels and available space on the map.

    The alternative is to put in a heap of transparent PNGs, but with more than 100 villages it becomes a bit cumbersome and a pain to produce all the images.

    Thanks,
    Kev
     
  2. emexes

    emexes Well-Known Member Licensed User

    I haven't done maps with B4A yet, but I imagine the markers are a list of icon/graphics and their longitude/latitude, which get overlaid by OSMDroid on to the map.

    You could create the transparent images from your text list of villages by Canvas.DrawText'ing the village name to a new BitMap (icon?), and passing the result plus longitude/latitude to OSMDroid.

    Still mildly cumbersome, but no longer a pain.
     
  3. Kevin Hartin

    Kevin Hartin Member Licensed User

    So canvas.bitmap can create an image from text and then I can save it as a transparent png?
     
  4. emexes

    emexes Well-Known Member Licensed User

    It'll certainly make nice bitmaps with a transparency channel that you should be able to pass to OSMDroid, and I expect that they can be saved as PNG files (but I haven't done it).
     
  5. emexes

    emexes Well-Known Member Licensed User

    The sample code of the post Add a MarkersOverlay layer to the MapView suggests that you can just pass the Bitmap to OSMDroid, no need to write it to a file and read it back (although in this example, he is loading the Bitmap from a file rather than creating the Bitmap programmatically).
    Code:
    '   for Marker1 i'll use a custom icon
    Dim Icon As BitmapDrawable
    Icon.Initialize(
    LoadBitmap(File.DirAssets, "my_icon.png"))
     
    Dim Marker1 As Marker
    Marker1.Initialize(
    "Home sweet home""Lorem ... molestie."52.756100.39748, Icon)
    edit: it also suggests that the author is a mad soccer fanatic
     
  6. emexes

    emexes Well-Known Member Licensed User

    There was something recently about the trips and traps of measuring text size, and I've mucked around a little, and... yeah, the MeasureStringXxx routines are not pixel-accurate.

    So I've gotten as far as painting the text to a BitMap, and then scanning the BitMap to find the outermost non-background pixels, and then drawn a red border two pixels out from that (one pixel padding and one pixel border width), and it looks like this:

    TempTextMeasurement.png

    but it's Thursday night = Professional Gamblers Dinner Night = I gotta get moving ;-)

    Between the sample code above and below, and knowing that there is a Canvas.DrawBitmap method, you should be on the home straight with respect to generating markers in memory to pass to OSMDroid.

    Code:
    Sub MakeTextBitmap(S As String)

        
    Log("MakeTextBitmap " & S)
       
        
    Dim B As Bitmap
        B.InitializeMutable(
    300,300)
       
        
    Dim C As Canvas
        C.Initialize2(B)
       
        
    Dim R As Rect
        R.Initialize(
    00300300)
       
        C.DrawRect(R, 
    Colors.ARGB(64,128,128,64),True,0)
       
        
    Dim TextWidth As Float = C.MeasureStringWidth(S,Typeface.SANS_SERIF, 15)
        
    Dim TextHeight As Float = C.MeasureStringHeight(S, Typeface.SANS_SERIF, 15)
       
        
    Log(TextWidth & " " & TextHeight)
       
        C.DrawText(S, 
    55+TextHeight, Typeface.SANS_SERIF, 15Colors.Yellow, "LEFT")
       
        
    Dim MinX As Int = 9999
        
    Dim MaxX As Int = -9999
        
    Dim MinY As Int = 9999
        
    Dim MaxY As Int = -9999
       
        
    Dim BlankPixel As Int = B.GetPixel(00)
        
    For X = 0 To B.Width - 1
            
    For Y = 0 To B.Height - 1
                
    If B.GetPixel(X, Y) <> BlankPixel Then
                    
    If X < MinX Then MinX = X
                    
    If X > MaxX Then MaxX = X
                    
    If Y < MinY Then MinY = Y
                    
    If Y > MaxY Then MaxY = Y
                
    End If
            
    Next
        
    Next
       
        
    Log (MinX & " " & MinY & " " & MaxX & " " & MaxY)
       
        R.Initialize(MinX - 
    2, MinY - 2, MaxX + 2, MaxY + 2)
        C.DrawRect(R, 
    Colors.Red, False1)
       
        
    Dim IV As ImageView
        IV.Initialize(
    "")
        IV.Bitmap = B
        IV.Visible = 
    True
        
    Activity.AddView(IV,100100, B.Width, B.Height)
       
    End Sub
     
  7. Kevin Hartin

    Kevin Hartin Member Licensed User

    OK, been playing with the canvas bitmap thing and it works exactly how I want it to.

    HOWEVER....

    My list of villages is over 200 at the moment and will get to over 300 eventually, so I need to be able to get them from my Db and dynamically loop through creating each Bitmap and then each marker.

    I can read the DB easily and have done so for other markers, which all share the same Bitmap Icon, however I am stuck as to how to do this dynamically without Dim IconName as Bitmap for every one of the 200 plus villages.

    I am sure it an easy thing, but as a newbie hack programmer, I am stuck...

    Attached is a test project with the DB

    Thanks,
    kev
     

    Attached Files:

  8. emexes

    emexes Well-Known Member Licensed User

    That is looking pretty good. I think you'd be better off without having the global Bitmap B though, ie just have the Make function return what it's made:
    Code:
    Sub MakeTextBitmap(S As StringAs Bitmap
     
        
    Log("MakeTextBitmap " & S)

        
    dim C as Canvas    'used twice below

        
    Dim TempBitmap As Bitmap
        TempBitmap.InitializeMutable(
    1dip1dip)    'I am pretty sure that size doesn't matter here
        C.Initialize2(TempBitmap)
     
        
    Dim TextWidth As Float = C.MeasureStringWidth(S, Typeface.SANS_SERIF, 4)
        
    Dim TextHeight As Float = C.MeasureStringHeight(S, Typeface.SANS_SERIF, 4)
     
        
    Log("TEXT SIZE: " & TextWidth & " " & TextHeight)
     
        
    Dim NewBitMap As Bitmap
        NewBitMap.InitializeMutable(TextWidth + 
    4, TextHeight + 8)
        C.Initialize2(NewBitMap)
     
        C.DrawText(S, 
    22 + TextHeight, Typeface.SANS_SERIF, 4Colors.Black, "LEFT")
     
        
    Return NewBitMap
     
    End Sub
    and then refactoring your code correspondingly:
    Code:
    ' ### VILLAGES
    Dim RS1 As ResultSet
    RS1 = SQL1.ExecQuery(
    "SELECT village, latlon FROM villages")

    Dim MarkersList As List    'create list once, outside of loop
    MarkersList.Initialize

    Do While RS1.NextRow
        
    Dim VillageName As String = RS1.GetString("village")
        
    Dim VillageLatLon As String = RS1.GetString("latlon")
     
        
    If VillageLatLon.Contains(",") = False Then
            
    Log("WTF 58 - latlon no comma")
        
    End If
     
        
    Dim LatLon() As String = Regex.Split(",", VillageLatLon)
        
    Dim Lat As Double = LatLon(0)    'make sure these don't have N/S in them
        Dim Lon As Double = LatLon(1)    'make sure these don't have E/W in them

        
    Log(VillageName & "|" & VillageLatLon & "|" & Lat & "|" & Lon)
            
        
    Dim NewMarker As Marker
        NewMarker.Initialize(
    """", Lat, Lon, MakeTextBitmap(VillageName))
     
        MarkersList.Add(NewMarker)    
    'add marker to list
    Loop
    RS1.Close

    MarkersOverlay1.AddMarkers(MarkersList)
    Disclaimer: I haven't actually run this code, but hey: what could possibly go wrong?!?!
     
    Last edited: Apr 5, 2019
  9. emexes

    emexes Well-Known Member Licensed User

    I remember something about a list can be used like an array, or maybe it was an array can be used like a list, in which case you could dispense with MarkersList and just add the NewMarker directly to MarkersOverlay1 eg:
    Code:
    Do ...

        
    Dim NewMarker as Marker
        NewMarker.Initialize(...)

        MarkersOverlay1.AddMarkers(
    Array as Marker(NewMarker))
    Loop
    or maybe more simply but less explicitly as:
    Code:
    Do ...

        
    Dim NewMarker(1as Marker    'NewMarker is an ARRAY of one marker
        NewMarker(0).Initialize(...)

        MarkersOverlay1.AddMarkers(NewMarker)
    Loop
    Again: what could possibly go wrong?!?!?!

    ;-)
     
    Last edited: Apr 5, 2019
  10. Kevin Hartin

    Kevin Hartin Member Licensed User

    emexes, you are a bloody champion. 99% right!!!

    Dim NewMarker As Marker
    Dim TextIcon As BitmapDrawable
    TextIcon.Initialize(MakeTextBitmap(VillageName))

    NewMarker.Initialize("", "", Lat, Lon, TextIcon)

    Your sub returned a Bitmap object, when a BitMapDrawable was needed, so I added the stuff in Red.

    The TextMarkers populate really fast, but at Zoom level 14 and below they are a mess on top of each other, so I'll group them into the main Villages for Zoom 10-12, a few more from 12-14 and the whole lot for 14+.

    Now I just got to stop dreaming up features to add and do a tidy up so I can release 1.0 asap.

    Thanks again,
    Kev
     
  11. emexes

    emexes Well-Known Member Licensed User

    or perhaps I like to leave at least one typo in each sample code to test whether it's being used or not ;-)

    I was thinking you could add them in order from highest-to-lowest population, and for each marker make sure that no previous marker is nearby, but I think your idea of giving each village a minimum-zoom-level-required might work better, given that each zoom level is a specific pixels-to-ground scale (I think... starts with zoom 0 = 256 pixels = 180 degrees of earth surface, something like that, wasn't it? or maybe I'm confusing it with something else)

    You're not alone with that "fault" ;-)

    I went looking for the Steve Jobs "Real Artists Ship" quote, found this instead, seems apt given we're talking maps:

    quote-steve-jobs-all-over-the-map-focus.jpg
     
  12. emexes

    emexes Well-Known Member Licensed User

    Hey, I was just thinking about ways to get the village and other POI data for your app, and you're probably already on to this, but just in case: it's pretty easy to edit and add to OpenStreetMap, eg bike-friendly repair and coffee shops, rest areas, toilets, lookouts, bike traps and tricks, etc. And you can add info like village population and alternate/local names, shop phone numbers and hours and services, etc.

    I downloaded some data (is XML = bulky but readable). Somebody called Palolo has been hard at work. The data for a random village looks like:

    Code:
    <node id="1446311758" visible="true" version="2" changeset="64601338" timestamp="2018-11-17T17:08:33Z" user="pizzaiolo" uid="1772368" lat="-13.5242459" lon="-172.3058240">
      <tag k=
    "name" v="Patamea"/>
      <tag k=
    "place" v="village"/>
      <tag k=
    "wikidata" v="Q16896450"/>
     </
    node>
    and that wikidata value leads to this page which looks like a set of data that might contain useful stuff eg link to Wikipedia entry. But perhaps this is all stuff to leave for Version 2.
     
  13. Kevin Hartin

    Kevin Hartin Member Licensed User

    Haha, guilty as charged.

    HOWEVER...

    I got all excited prematurely . My test app used OSMDroid 3.0.8??? While my app uses 4.??.

    It seems that the application of icons to markers is different and at first glance, somewhat more restrictive with an icon being set to the marker sets rather to each marker that makes up the set.

    Need to look further into.it or revert to OSMDroid 3.??.

    Tomorrow... it's beer o'clock in Samoa. I know I got one of them restless nights sleep ahead thinking about this stuff.

    Kev
     
  14. emexes

    emexes Well-Known Member Licensed User

    That symptom sounds like the same as would happen if each marker was being pointing to the same icon, so... hmm.
     
  15. emexes

    emexes Well-Known Member Licensed User

    And this page here has two relevant items:


    Marker using a text label instead of an icon. The marker class can render a simple text label for icons without an icon.

    Say what?!?! Perhaps the text-to-icons phase is not necessary (although it's been fun ;-)


    Fast Overlay - The fast overlay is great if you have a huge number points to render and they all share the same icon.

    The "same icon" stipulation suggests that non-fast overlays can use different icons.


    edit: although I'm not sure which version/port of OSMdroid of this is; the samples in Java don't bode well for it being the B4A instance
     
  16. Kevin Hartin

    Kevin Hartin Member Licensed User

    Not sure if the Text option is available in the OSMdroid libraries.

    Anyway, I spent an hour this morning reverting back to 3.0.8 and have got both Text and Icons going just fine.

    HOWEVER...

    My "Static" Pin icons are stretched to be huge, despite being only 30x30 pixels. currently searching for a fix to the huge icons.

    And my offline maps are not being read now. GRRR!
     
  17. Kevin Hartin

    Kevin Hartin Member Licensed User

    Got OSMdroid 4.xx going. There is a separate method??? needed to allocate a bitmap to the marker before it get added to the list.

    Interestingly, OSMd 3.xx has the text nicely sized at 4pt but my 30x30 icons get stretched, while OSMd 4.xx I had to increase the text to 10pt, while my icons seem to be crips at 30x30.

    Anyway, enough of the new features, time to clean up and publish.

    Thanks for all the help,
    kev
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice