6

Speaking with a cartographer from National Geographic last night, my interest was again piqued (peaked?) in an open-source map publishing toolset built on PostGIS and Inkscape (as per his workflow and how others use Illustrator with Avenza Map Publisher using ArcGIS data)

With the PostGIS ST_AsSVG command, I'm not sure how to work with the output as an SVG file. I can see in other discussions that SVG's can be exported somehow, but I'm not sure how that works exactly.

With that said, assuming it's perhaps just a matter of orienting the command to export a static SVG file - would/could it even be possible for Inkscape to read an SVG directly out of PostGIS?

  • 1
    I believe the problem is that you only get the geometry from ST_AsSVG (from my attempts, anyway). So, if you want to apply any interesting styling rules based on some other attributes, you are a bit stuck. Also, you have to add the header and footer yourself, which of course you can do in the same SQL statement. – John Powell Feb 11 '15 at 18:18
  • @JohnBarça what are the header and footer? Also: ok - I was wondering if you could get any other data besides geometry... so this may not be possible after all... – DPSSpatial Feb 11 '15 at 18:20
  • 1
    If you look at a really simple example, you'll see that you have opening svg tags which contains a width and a height (and optionally other info on scale and location). This is pretty easy to hand craft. The real killer is the lack of attributes directly inside what is returned from ST_AsSVG. So, you would have to concatenate the geometry along with anything else you wanted to to include for styling, which you could do with a function. – John Powell Feb 11 '15 at 18:28
  • 1
    Check out, select concat('<path d= "', st_assvg(st_buffer(st_makepoint(0,0),5)), '" />'); If you replace the st_buffer(st_makepoint(... with an actual query against a geometry you will get a bunch of paths inside svg tags. You can then use array_agg in conjunction with array_to_string to combine together. You could insert attributes in a similar fashion. – John Powell Feb 11 '15 at 18:43
  • Haven't tried it, but TileMill will let you style your PostGIS data and export it as SVG vector. Don't know your workflow, so maybe that's overkill for what you want to do. – Scro Feb 11 '15 at 18:59
9

The Postgis function ST_AsSVG simply converts geometries into their SVG point or path equivalents, and does not allow you to add any attributes/styling directly. For example, a query with a Point, Linestring and Polygon:

WITH shapes (geom, attribute) AS 
   (VALUES(
      (SELECT ST_MakePoint(0,0)),1),
      ((ST_MakeLine(ST_MakePoint(0,0), ST_MakePoint(10,10))), 2), 
      ((SELECT ST_MakeBox2d(ST_MakePoint(0,0), ST_MakePoint(10,10))), 3)
   )
SELECT ST_AsSVG(geom) from shapes;

returns:

 cx="0" cy="0"
 M 0 0 L 10 -10
 M 0 0 L 0 -10 10 -10 10 0 Z

which is a point, an unclosed linestring and a closed line, ie, a polygon. M means move to, L means line to and Z means close. There are other geometry types in SVG, but this is what ST_AsSVG returns (see docs for the Multi version of the same).

By itself, this isn't very helpful, as you are missing the path tags and any styling information, a full example of which would look something like:

 <path d="M 175 200 l 150 0" stroke="green" stroke-width="3" fill="none" />
 <path d="M 100 350 q 150 -300 300 0" stroke="blue" stroke-width="5" fill="none" />

See w3schools SVG Tutorial for more information.

So, if you want to actually create valid SVG using a Postgis query, you will need to add path tags and color, line, fill, etc formatting. This can be accomplished by using the concat operator and a case statement to set the formatting. For example,

WITH shapes (geom, attribute) AS (
  VALUES(
    (SELECT ST_MakeLine(ST_MakePoint(0,0), ST_MakePoint(10,10))), 2),
    ((SELECT ST_Envelope(ST_MakeBox2d(ST_MakePoint(0,0), ST_MakePoint(10,10)))), 3)
  ) 
  SELECT 
      concat(
         '<path d= "', 
         ST_AsSVG(geom,1), '" ', 
         CASE WHEN attribute = 0 THEN 'stroke="red" stroke-width="3" fill="none"' 
         ELSE 'stroke="black" stroke-width="2" fill="green"' END,
         ' />') 
 FROM shapes;

which returns:

<path d= "M 0 0 l 10 -10" stroke="black" stroke-width="2" fill="green" />
<path d= "M 0 0 l 0 -10 10 0 0 10 z" stroke="black" stroke-width="2" fill="green" />

Finally, if you wrap all of that in array_to_string and array_agg, and add the head and footer, ie, the <svg height="400" width="450"> and </svg>, you will end up with one block of SVG representing all your geometries, eg,

WITH shapes (geom, attribute) AS (
  VALUES(
    (SELECT ST_MakeLine(ST_MakePoint(0,0), ST_MakePoint(10,10))), 2),
    ((SELECT ST_Envelope(ST_MakeBox2d(ST_MakePoint(0,0), st_makepoint(10,10)))), 3)
  ),
  paths (svg) as (
     SELECT concat(
         '<path d= "', 
         ST_AsSVG(geom,1), '" ', 
         CASE WHEN attribute = 0 THEN 'stroke="red" stroke-width="3" fill="none"' 
         ELSE 'stroke="black" stroke-width="2" fill="green"' END,
          ' />') 
     FROM shapes
 )
 SELECT concat(
         '<svg height="400" width="450">',
         array_to_string(array_agg(svg),''),
         '</svg>')
 FROM paths;

which now returns the single record:

<svg height="400" width="450"><path d= "M 0 0 l 10 -10" stroke="black" stroke-width="2" fill="green" /><path d= "M 0 0 l 0 -10 10 0 0 10 z" stroke="black" stroke-width="2" fill="green" /></svg>

TL;DR, other options

At which point you are probably thinking, TL;DR, and it is true this is a lot of work, and the case statements to make the stroke color, width, etc, could get very long and unwieldy.

There are other options. Geoserver (with a Postgis/vector data source) supports SVG as an output format and you can use SLDs or CartoCSS within Geoserver to style them up, then just request a WMS with SVG as output format and you are done. As @Scro also said, you can use tools like TileMill to do the styling for you (not tested). So, after a very long answer, I am basically suggesting that you should not use ST_AsSVG directly for anything other than very quick and dirty SVGs.

1

If you are open to exporting data from within QGIS, rather than from PostGIS, you could use the Simple SVG plugin. This plugin will export the active layers from within QGIS, attempting to maintain the symbology. I have uses this with some relatively good success. Additionally, you can use QGIS composer to start your maps and use their SVG export function. This is not the same as the PostGIS query, but if the goal is to get data from QGIS to Inkscape, then this will work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.