Mapnik: Erzeugen von kombinierter Beschriftung aus mehreren Feldern

Hallo,

wie ich ja in letzter Zeit schon geschrieben habe bin ich gerade dabei eine spezielle Hydrantenkarte für eine Feuerwehr zu erstellen. Ich habe dafür Mapnik2 auf einem Ubuntu-Server laufen.

Ich habe die OpenFireMap-Quellen erhalten und angefangen den Kartenstil etwas zu modifizieren bzw. zu erweitern (in Zoomstufe 18 soll außerdem der Druck und die Durchflussrate angegeben werden), aber irgendwie funktioniert es nicht so wirklich. Hier ein Auszug des Inhalts meiner osm.xml:


[...]
<Style name="hydrants">
[...]
    <Rule>
      &maxscale_zoom18;
      &minscale_zoom18;
      <Filter>[fh_t] = 'underground'</Filter>
      <PointSymbolizer file="&symbols;/hydrant_u_17.png" allow-overlap="true"/>
      <TextSymbolizer allow-overlap="true" fontset-name="bold-fonts" size="9" fill="#ff0000" dy="9" halo-radius="1" wrap-width="0">H[fh_d] \n [fh_p] bar \n [fh_f]</TextSymbolizer>
    </Rule>
</Style>

<Layer name="hydrants" status="on" srs="&osm2pgsql_projection;">
    <StyleName>hydrants</StyleName>
    <Datasource>
      <Parameter name="table">
      (select way,name,emergency,amenity,ref,
      coalesce("fire_hydrant:type",'_') as fh_t,replace(replace("fire_hydrant:diameter",'fixme',' '),'FIXME',' ') as fh_d,
      "fire_hydrant:pressure" as fh_p,
      case when "fire_hydrant:flow_capacity" similar to '[[:digit:]]+' then
      round(cast("fire_hydrant:flow_capacity" as numeric)*60) || ' l/min' else null end as fh_f
      from &prefix;_point
      where ( emergency='fire_hydrant' or amenity='fire_hydrant' )
      ) as hydrants
      </Parameter>
      &datasource-settings;
    </Datasource>
</Layer>
[...]

Das \n erzeugt laut Dokumentation einen Zeilenumbruch (wie auch in “richtigen” Programmiersprachen). Ich habe es auch schon ohne probiert, aber auch da funktioniert es nicht.

Wenn ich renderd mit der Option -f starte bekomme ich folgenden Fehler ausgegeben:

renderd[29494]: An error occurred while loading the map layer 'default': Failed to parse expression: "H[fh_d]" in TextSymbolizer in style 'hydrants' in map '/etc/mapnik-osm-data/osm.xml'

Anscheinend kann ich also bei nur eine Variable angeben, z.B. [fh_d] (so war es auch in der ursprünglichen OpenFireMap-Datei).

Kann mir jemand einen Tipp geben wo ich ansetzen muss damit ich die Sache zum Laufen kriege?

Vielen Dank schonmal,

Christoph

glaube schon: formuliere deine Abfrage etwa so:


(select way,
        name,
        emergency,
        amenity,
        ref,
        'H' || coalesce("fire_hydrant:type",'_') || '\n' || 
            replace(replace("fire_hydrant:diameter",'fixme',' '),'FIXME',' ') || 
            "fire_hydrant:pressure" ||
        case 
           when "fire_hydrant:flow_capacity" similar to '[[:digit:]]+' then
              round(cast("fire_hydrant:flow_capacity" as numeric)*60) || ' l/min' 
           else 
              null 
        end as fh_full
   from &prefix;_point
  where ( emergency='fire_hydrant' or amenity='fire_hydrant' )
        ) as hydrants;

<TextSymbolizer allow-overlap="true" 
                fontset-name="bold-fonts" 
                size="9" 
                fill="#ff0000" 
                dy="9" 
                halo-radius="1" 
                wrap-width="0">
  [fh_full]
</TextSymbolizer>

Knackpunt ist, dass Postgresql EIN Feld fh_full zurückgibt, das dann im textsymbolizer verwendet wird.
es könnten noch 1-2 tippfehler drin sein, aber das Prinzip sollte klar sein.

Gruss
walter

Hallo Walter,

vielen Dank schonmal für deine Arbeit!

Allerdings hab ich jetzt noch das Problem, dass [fh_t] dafür benutzt wurde zu unterscheiden welche Grafik für den jeweiligen Hydranten angezeigt wird (Unterscheidung in der Bauform unterirdisch <-> oberirdisch). Ich dachte dann dass ich einfach beide Stile drinlasse, denn ich wollte in Zoomstufe 17 nur die Bauart und den Durchmesser und in Zoomstufe 18 komplett alles anzeigen lassen. Mein Lösungsansatz


    <Rule>
      &maxscale_zoom17;
      &minscale_zoom17;
      <Filter>[fh_t] = 'underground'</Filter>
      <PointSymbolizer file="&symbols;/hydrant_u_17.png" allow-overlap="true"/>
      <TextSymbolizer allow-overlap="true" fontset-name="bold-fonts" size="9" fill="#ff0000" dy="9" halo-radius="1" wrap-width="0">[fh_d]</TextSymbolizer>
    </Rule>
    <Rule>
      &maxscale_zoom18;
      &minscale_zoom18;
      <Filter>[fh_t] = 'underground'</Filter>
      <PointSymbolizer file="&symbols;/hydrant_u_17.png" allow-overlap="true"/>
      <TextSymbolizer allow-overlap="true" fontset-name="bold-fonts" size="9" fill="#ff0000" dy="9" halo-radius="1" wrap-width="0">[fh_full]</TextSymbolizer>
    </Rule>

<Layer name="hydrants_17" status="on" srs="&osm2pgsql_projection;">
    <StyleName>hydrants</StyleName>
    <Datasource>
      <Parameter name="table">
      (select way,name,emergency,amenity,ref,
      coalesce("fire_hydrant:type",'_') as fh_t,replace(replace("fire_hydrant:diameter",'fixme',' '),'FIXME',' ') as fh_d
      from &prefix;_point
      where ( emergency='fire_hydrant' or amenity='fire_hydrant' )
      ) as hydrants
      </Parameter>
      &datasource-settings;
    </Datasource>
</Layer>
<Layer name="hydrants_18" status="on" srs="&osm2pgsql_projection;">
    <StyleName>hydrants</StyleName>
    <Datasource>
      <Parameter name="table">
      (select way,
        name,
        emergency,
        amenity,
        ref,
        'H' || coalesce("fire_hydrant:type",'_') || '\n' || 
            replace(replace("fire_hydrant:diameter",'fixme',' '),'FIXME',' ') || 
            "fire_hydrant:pressure" ||
        case 
           when "fire_hydrant:flow_capacity" similar to '[[:digit:]]+' then
              round(cast("fire_hydrant:flow_capacity" as numeric)*60) || ' l/min' 
           else 
              null 
        end as fh_full
   from &prefix;_point
  where ( emergency='fire_hydrant' or amenity='fire_hydrant' )
        ) as hydrants
      </Parameter>
      &datasource-settings;
    </Datasource>
</Layer>

(es sind noch weitere Regeln vorhanden die eigentlich alle gleich aufgebaut sind, die habe ich aber aufgrund der Übersichtlichkeit ausgeblendet)

hat dann aber irgendie nicht funktioniert - ich bekomme beim Aufruf von renderd die Fehlermeldung

ERROR:  column "fh_d" does not exist
LINE 1: SELECT AsBinary("way") AS geom,"fh_d","fh_t" from 
                                       ^
Full sql was: 'SELECT AsBinary("way") AS geom,"fh_d","fh_t" from 
      (select way,
        name,
        emergency,
        amenity,
        ref,
        'H' || coalesce("fire_hydrant:type",'_') || '\n' || 
            replace(replace("fire_hydrant:diameter",'fixme',' '),'FIXME',' ') || 
            "fire_hydrant:pressure" ||
        case 
           when "fire_hydrant:flow_capacity" similar to '[[:digit:]]+' then
              round(cast("fire_hydrant:flow_capacity" as numeric)*60) || ' l/min' 
           else 
              null 
        end as fh_full
   from planet_osm_point
  where ( emergency='fire_hydrant' or amenity='fire_hydrant' )
        ) as hydrants
       WHERE "way" && SetSRID('BOX3D(890185.6314091616 6364299.849080344,892937.36442743 6367051.582098612)'::box3d, 900913)'

Weist du hier vielleicht auch noch einen Rat?

Viele Grüße und nochmal Danke!

Christoph

                                       
Full sql was: 'SELECT AsBinary("way") AS geom,"fh_d","fh_t" from 
      (select way,
        name,
        emergency,
        amenity,
        ref,
        abc as fh_d,                                 <------------------- hier
        xyz as fh_t,                                 <------------------- erweitern
        'H' || coalesce("fire_hydrant:type",'_') || '\n' || 
            replace(replace("fire_hydrant:diameter",'fixme',' '),'FIXME',' ') || 
            "fire_hydrant:pressure" ||
        case 
           when "fire_hydrant:flow_capacity" similar to '[[:digit:]]+' then
              round(cast("fire_hydrant:flow_capacity" as numeric)*60) || ' l/min' 
           else 
              null 
        end as fh_full

   from planet_osm_point
  where ( emergency='fire_hydrant' or amenity='fire_hydrant' )
        ) as hydrants
       WHERE "way" && SetSRID('BOX3D(890185.6314091616 6364299.849080344,892937.36442743 6367051.582098612)'::box3d, 900913)'

jo, etwas unhübsch, aber: hole dir fh_d,fh_t und fh_full von der db ab. abc und xyz natürlich ähnlich wie bei fh_full anpassen.
irgendwie doppelt gemoppelt aber was besseres fällt mir auf die schnelle auch nicht ein.

Danke für die Antwort. Ich hab noch ein bisschen umgebaut, und jetzt ist es (fast) perfekt:


<Layer name="hydrants" status="on" srs="&osm2pgsql_projection;">
    <StyleName>hydrants</StyleName>
    <Datasource>
      <Parameter name="table">
      (select way,
        name,
        emergency,
        amenity,
        ref,
	"fire_hydrant:type" as fh_t,
	'H' || replace(replace("fire_hydrant:diameter",'fixme',' '),'FIXME',' ') as fh_d,
        'H' || replace(replace("fire_hydrant:diameter",'fixme',' '),'FIXME',' ') || '
' ||
        floor(cast("fire_hydrant:pressure" as numeric)*10)*0.1 || ' bar' || '
' ||
        floor(cast("fire_hydrant:flow_capacity" as numeric)*60*0.01)*100 || ' l/min'
        as fh_full
   from &prefix;_point
  where ( emergency='fire_hydrant' or amenity='fire_hydrant' )
        ) as hydrants
      </Parameter>
      &datasource-settings;
    </Datasource>
</Layer>

als Karte sieht es dann so aus:

http://ffebg.no-ip.org:911/osm/slippymap.html?zoom=18&lat=49.53708&lon=8.01906&layers=0B0 (rechts oben den Layer “FF Ebg externer Zugriff” wählen, hab nur die Daten für unseren Landkreis und noch etwas Gebiet drumrum auf dem Server da dieser schon etwas älter ist. Also nicht wundern wenn der eigene Ort nicht auf der Karte erscheint)

Vielen Dank nochmal an wambacher für die schnelle und kompetente Hilfe!

Zum “(fast) perfekt”: Gibt es eine Möglichkeit einer if-else-abfrage? In den ersten Posts hatte ich ja noch einer “when “fire_hydrant:flow_capacity” similar to ‘[[:digit:]]+’ then”-Abfrage drin, aber die funktioniert irgendwie nicht… Ich will einfach nur prüfen ob z.B. der Druck des Hydranten angegeben ist und wenn nicht dann diesen nicht verwenden. Ansonsten wird nämlich nur das Symbol gezeichnet, aber Der Leitungsdurchmesser und die Durchflussrate werden nicht angezeigt, auch wenn diese vorhanden sind und halt nur der Druck nicht.

Viele Grüße,

Christoph

wenn es um Mapnik geht, muß ich passen. ich “spreche” nur Sql :wink:
in sql gibt es kein if sondern das wird alles mit case erledigt - aber das ist jetzt wohl nicht dein Problem.

Gruss
walter

http://www.postgresql.org/docs/9.1/static/functions-conditional.html

Zeiter Satz aus deinem Link:

Ok, damit wäre das geklärt. Dann muss ich mit CASE experimentieren. Das wird aber gegenüber der vorherigen Probleme die leichteste Übung sein.

Danke nochmal!

Keine Ursache.

Wie sagten wir früher: “Danksagungen bitte in schriftlicher Form an unseren Chef - Fehlermeldungen und Beschwerden bitte in mündlicher Form an unseren Hausmeister” :wink: