Osmosis. Use bounding box filter in java code

303 views Asked by At

I am importing a osm.pbf file into my java application using Osmosis like this:

data class MapObject (
    val lat: Double,
    val lon: Double,
    val id: Long
)

class OsmReader : Sink {
    val data = mutableListOf<MapObject>()

    override fun close() {}

    override fun complete() {}

    override fun initialize(metaData: MutableMap<String, Any>?) {}

    override fun process(entityContainer: EntityContainer?) {
        if (entityContainer is NodeContainer) {
            val node = entityContainer.entity

            data.add( MapObject(node.latitude, node.longitude, node.id) )
        }
    }
}

fun readOSM(pathToPBF: String) {
    val inputStream = FileInputStream(pathToPBF)

    // read from osm pbf file:
    val custom = OsmReader()
    val reader = OsmosisReader(inputStream)
    reader.setSink(custom)
    // initial parsing of the .pbf file:
    reader.run()
    println("Break")
}

I would like to only read the nodes in a given bounding box. Then using Osmosis from the command line you can use --bounding-box, but how would you do the same thing in code?

Currently I intersect each node with the bounding box using the JTS, however that is rather slow.

2

There are 2 answers

0
Jacopo On

I found out the answer now:

Here is an example of how you would get all the shops in a specific bouding box and store them in lists for the nodes, ways, and relations in lists.

I use kotllin with gradle.

In the gradle.kts:

dependencies {
    implementation("org.openstreetmap.osmosis:osmosis-pbf:0.48.3")
    implementation("org.openstreetmap.osmosis:osmosis-areafilter:0.48.3")
}

The code:

import crosby.binary.osmosis.OsmosisReader
import org.openstreetmap.osmosis.areafilter.v0_6.BoundingBoxFilter
import org.openstreetmap.osmosis.core.container.v0_6.*
import org.openstreetmap.osmosis.core.domain.v0_6.*
import org.openstreetmap.osmosis.core.filter.common.IdTrackerType
import org.openstreetmap.osmosis.core.task.v0_6.Sink
import java.io.FileInputStream

class CustomProcessor : Sink {
    // Here the results will be stored
    val nodes = mutableListOf<Node>()
    val ways = mutableListOf<Way>()
    val relations = mutableListOf<Relation>()

    override fun close() {
        // Close all your readers and such
    }

    override fun initialize(metaData: MutableMap<String, Any>?) {
        // Don't know when to use this
    }

    override fun complete() {
        // If you first put data into storages you can process them here after you have all you need
    }

    override fun process(entityContainer: EntityContainer?) {
        val entity = entityContainer?.entity

        // Filter for shops
        var foundIt = false
        if (entity != null) {
            for (tag in entity.tags) {
                if (tag.key == "shop") {
                    foundIt = true
                    break
                }
            }
        }

        // If the object is a shop
        if (foundIt) {
            if (entity is Node) {
                nodes.add(entity)
            } else if (entity is Way) {
                ways.add(entity)
            } else if (entity is Relation) {
                relations.add(entity)
            } else if (entity is Bound) {
                // We don't store bounds
            } else {
                // Shouldn't be possible
            }
        }
    }
}

fun main(args: Array<String>) {
    // Set input stream
    val inputStream = FileInputStream("path/to/my/file.osm.pbf")

    // Create the bounding box filter
    val bbFilter = BoundingBoxFilter(
        IdTrackerType.Dynamic, // ID tracker used internally. This is the default.
        11.5256, // Left lon
        11.5004, // Right lon
        48.84543, // Top lat
        48.8015, // Bottom lat
        true, // Remove ways and relation only partly in bounding box?
        false, // Include all nodes of ways only partly in bounding box.
        false, // Include all nodes and ways of relations only partly in bounding box.
        false // Include all relations that reference relation inside bounding box.
    )

    // Create your processor
    val processor = CustomProcessor()

    // Define your pipeline
    val reader = OsmosisReader(inputStream)
    bbFilter.setSink(processor)
    reader.setSink(bbFilter)

    // Run
    reader.run()

    // Get your data
    val nodes = processor.nodes
    val ways = processor.ways
    val relations = processor.relations
}

If you want the geometries of ways and relations you will also need to store the nodes they consist of. These are not tagged with "shop". Storing them in lists is quite RAM intensive so it is best to use the org.openstreetmap.osmosis.core.store.SimpleObjectStore class.

1
Jesper On

I tried using the pipeline which is described in the answer from Jacopo, for me the way the pipeline was constructed made it not filter the given pbf file.

What did work for me is the following piece of code.

// Set input stream
val inputStream = FileInputStream("path/to/my/file.osm.pbf")

// Create the bounding box filter
val bbFilter = BoundingBoxFilter(
    IdTrackerType.Dynamic, // ID tracker used internally. This is the default.
    11.5256, // Left lon
    11.5004, // Right lon
    48.84543, // Top lat
    48.8015, // Bottom lat
    true, // Remove ways and relation only partly in bounding box?
    false, // Include all nodes of ways only partly in bounding box.
    false, // Include all nodes and ways of relations only partly in bounding box.
    false // Include all relations that reference relation inside bounding box.
)

// Create your processor
val processor = CustomProcessor()

// Define your pipeline
val reader = OsmosisReader(inputStream)
bbFilter.setSink(processor)
reader.setSink(bbFilter) // This part is important in using the BoundingBoxFilter

// Run
reader.run()

Some extra information about the way this pipeline works.

When running the OsmosisReader it runs the process method in the given sink, which in this case is the boundingboxfilter, the boundingboxfilter then filters the given data and calls the process method from the given sink, which in this case is the customprocessor, this way only the items which are inside the bounding box will reach the CustomProcessor.

Hopefully this answers helps other people looking for information how to use the boundingboxfilter from osmosis.