mapnik / mapnik-support

Use the issues queue here to ask questions and offer help on using Mapnik (maybe if this works well we can retire the mailing list at http://mapnik.org/contact/?)
6 stars 6 forks source link

Can't get points/markers to overlap #134

Open larsga opened 4 years ago

larsga commented 4 years ago

I'm trying to generate maps showing ethnographic data, to be published in a book.

This is inside another Python framework, but the code should be reasonably clear, I think:

    ctx = mapnik.Context()

    symbol_layers = {}
    for symbol in themap.get_symbols():
        svgfile = '/tmp/%s.svg' % symbol.get_id()
        generate_svg(svgfile, symbol)

        # tried this, didn't work, switched to MarkersSymbolizer
        # ps = mapnik.PointSymbolizer()
        # ps.file = svgfile
        # ps.allow_overlap = True
        # ps.ignore_placement = True

        ps = mapnik.MarkersSymbolizer()
        ps.file = svgfile
        ps.allow_overlap = True
        ps.ignore_placement = True
        ps.spacing = 1
        ps.placement = 'point'
        ps.multi_policy = 'each'

        r = mapnik.Rule()
        r.symbols.append(ps)

        s = mapnik.Style()
        s.rules.append(r)

        m.append_style('symbol_' + symbol.get_id(), s)

        ds = mapnik.MemoryDatasource()
        symbol_layers[symbol.get_id()] = ds

        l = mapnik.Layer('%s_layer' % symbol.get_id())
        l.datasource = ds
        l.styles.append('symbol_' + symbol.get_id())

        m.layers.append(l)

    for marker in themap.get_markers():
        f = mapnik.Feature.from_geojson(json.dumps({
          "type": "Feature",
          "geometry": {
            "type": "Point",
            "coordinates": [float(marker.get_longitude()), float(marker.get_latitude())]
          }
        }), ctx)
        symbol_layers[marker.get_symbol().get_id()].add_feature(f)

The logic is that I create one layer with a marker for each kind of symbol I need. Then I loop over my markers and add them to the layer for the kind of symbol they should be shown with.

In this particular case I have "red = beer is brewed by women", "blue = beer is brewed by men", and "pink = either/both".

My problem is if to circles touch each other, only one circle is shown. This is a problem for me, because it's basically hiding some of the data, so to the reader it looks like there's less data than there really is. The data is also often contradictory, and this is now obscured.

I've tried flipping the allows_overlap, ignore_placement, distance etc around in all the different combinations I can think of, but no go. Any ideas?

If I make the circles a little smaller then more of them are shown, but the map becomes harder to read.

larsga commented 4 years ago

This is generate_svg, in case it matters:

def generate_svg(filename, symbol):
    with open(filename, 'w') as f:
        size = symbol.get_scale() * 2
        mid = symbol.get_scale()

        f.write('''
            <svg viewBox="0 0 %s %s" xmlns="http://www.w3.org/2000/svg">
              <circle cx="%s" cy="%s" r="%s" stroke="%s" fill="%s"/>
            </svg>
        ''' % (
            size,
            size,
            mid,
            mid,
            mid,
            symbol.get_stroke_color(),
            symbol.get_color()
        ))