mousebird-consulting-inc / WhirlyGlobe

WhirlyGlobe Development
Other
830 stars 255 forks source link

Setting shader for vector tiles #1559

Closed Niklas81 closed 2 years ago

Niklas81 commented 2 years ago

I'm having the same issue as described in #1173. The solution works for me if adding the vectors using addVectors(_ vectors: [Any], desc: [AnyHashable : Any]?), by passing [kMaplyShader: kMaplyShaderDefaultLine] as desc argument.

However, I would like to load my maps using tiles, specifically vector tiles, thus leveraging all the benefits of MapboxVectorStyleSet. There seems to be at least two ways of telling this to use kMaplyShaderDefaultLine, however, none of them has any effect.

This roughly sums up what I'm doing:

if context.coordinator.mbVectorTilesFetcher == nil {
    context.coordinator.mbVectorTilesFetcher = MaplyMBTileFetcher(mbTiles: "myvectortiles")
} else if context.coordinator.styleset == nil {
    let styleSettings = MaplyVectorStyleSettings()
    styleSettings.arealShaderName = kMaplyShaderDefaultTriNoLighting // This seems like a place to specify shader

    let styleFileName = "style"
        guard let styleJSON = Bundle.main.url(forResource: styleFileName, withExtension: "json") else {
                log.error("No file named \(styleFileName).json")
                return
         }
        guard let jsonData = try? Data(contentsOf: styleJSON) else {
                log.error("No data in file \(styleFileName).json")
                return
         }
    context.coordinator.styleset = MapboxVectorStyleSet(json: jsonData, settings: styleSettings, viewC: context.coordinator.control!)
} else {
    let sampleParams = MaplySamplingParams()
    sampleParams.coordSys = MaplySphericalMercator(webStandard: ())
    sampleParams.singleLevel = true
    sampleParams.coverPoles = false
    sampleParams.edgeMatching = true
    sampleParams.minZoom = context.coordinator.mbVectorTilesFetcher!.minZoom()
    sampleParams.maxZoom = context.coordinator.mbVectorTilesFetcher!.maxZoom()
    sampleParams.forceMinLevel = true
    sampleParams.minImportance = 512*512
    sampleParams.minImportanceTop = 0

    guard let interp = MapboxVectorInterpreter(vectorStyle: context.coordinator.styleset!, viewC: context.coordinator.control!) else {
    return
    }

    guard let imageLoad = MaplyQuadImageLoader(params: sampleParams, tileInfo: context.coordinator.mbVectorTilesFetcher!.tileInfo(), viewC: context.coordinator.control!) else {
    return
    }

    let shader = MaplyShader()
    shader.name = kMaplyShaderDefaultLine           

    imageLoad.setTileFetcher(context.coordinator.mbVectorTilesFetcher!)

    imageLoad.setShader(shader) // This seems like another good place to specify shader
    imageLoad.setInterpreter(interp)
    imageLoad.baseDrawPriority = kMaplyImageLayerDrawPriorityDefault

}

Am I missing something, or is this a bug? Using XCFramework 3.5 as distributed here on Github.

mousebird commented 2 years ago

That isn't how you set up a shader, I'm afraid, and the MapboxVectorStyleSet has its own shaders in any case.

You want to look at the VectorMBTilesTestCase in the AutoTester app. That sets up a MaplyVectorStyleSimpleGenerator which provides a simple mapping from attribute data to visual representation. You'll want to make your own version of that object eventually, which returns custom MaplyVectorStyle objects based on attributes in the data.

Niklas81 commented 2 years ago

VectorMBTilesTestCase doesn't seem to demonstrate how to set a shader. However, doing this allows me to do just that:

let shader = context.coordinator.control?.getShaderByName(kMaplyShaderDefaultLine)
print(":: VALID shader? \(shader!.valid())") // outputs :: VALID shader? true
imageLoad!.setShader(shader!)

However, the problem with some countries being seen through the globe persists. I've looked at other issues here, that suggests this is because some objects are to large, and needs to be tessellated. I'm currently trying this approach, which is now giving me the opposite problem: the tessellation tool I'm using (tesselate in python library turfpy.transformation ) for pre-processing geoJson and producing tessellated polygons are making them too tiny, so they are dropped by tippecanoe when producing tiles. As an example, when Bolivia is tesselated, the one single polygon representing it in the geoJson becomes 1488 polygons when tesselated. One way of fixing this may be to just find a better command to run tippecanoe with, but I haven't found it..

Can you suggest a better method/workflow for pre-processing? This seems to be a fairly common problem. I've seen some issues here giving examples, but they all seem to relate to adding countries using .addVector rather than using vector mbtiles.

sjg-wdw commented 2 years ago

The shader for an image loader just affects the images. If you're using vectors, you need to use another mechanism, like what the MaplyVectorStyleSimpleGenerator does.

For tiles that end up heavily curved on the map, we've taken to rendering them into a background image. If you look at the MapboxKindaMap class, you'll see logic in there for "backgrounding" polygons.

Niklas81 commented 2 years ago

Ok, thanks. I found a way to make a form of tessellation work, so taking that route.