Display Vector Tile (PBF) in Azure Maps

769 views Asked by At

I am using Azure Maps, and want to display vector tiles that I have generated with tippecanoe.

I was initially getting an error in Azure Maps in the frontend javascript, which was basically complaining the generated pbf were not acceptable, because it was compressed. I fixed that.

Now, the frontend javascript does not throw any error, except when it cannot find a z/x/y pbf file.

When it does not throw any errors in the javascript console.log, it does not display any vector from the vector tile pbf.

What I think I am not getting right is the sourceLayer that goes into the javascript.

<script type="text/javascript">
        function InitMap() {
        console.log("Hi from InitMap")
            var map = new atlas.Map('myMap', {
                center: [-122.33, 47.6],
                zoom: 12,
                language: 'en-US',
                authOptions: {
                    authType: 'subscriptionKey',
                    subscriptionKey: '<My Azure Maps Key>'
                }
            });

            //Wait until the map resources are ready.
            map.events.add('ready', function () {
                //Create a vector tile source and add it to the map.
                datasource = new atlas.source.VectorTileSource(null, {
                    tiles: ['http://127.0.0.1:8080/api/file/{z}/{x}/{y}.pbf'],
                    maxZoom: 12
                });
                map.sources.add(datasource);

                var buildingLayer = new atlas.layer.PolygonLayer(datasource, null, {
                    sourceLayer: 'Feature',
                    fillColor: 'red',
                    fillOpacity: 0.7
                });
                map.layers.add(buildingLayer, 'labels');
            });
        }
    </script>

This is my Spring Boot that serves the Vector Tiles (PBFs) that were generated thru tippecanoe:

@ResponseBody
@CrossOrigin
@GetMapping("api/file/{z}/{x}/{y}.pbf")
public ResponseEntity<Resource>getPbf(
            @PathVariable("z") int z,
            @PathVariable("x") int x,
            @PathVariable("y") int y,
            HttpServletResponse httpServletResponse
        ) throws FileNotFoundException {
    
    File file = new File(String.format("/home/markus/Documents/AssessorTilesNotZipped/%d/%d/%d.pbf", z, x, y));
    System.out.println("Z: " + String.valueOf(z) + ", X: " + String.valueOf(x) + ", Y: " + String.valueOf(y));
    HttpHeaders headers = new HttpHeaders();
    String filename = file.getName();
    headers.add("Content-disposition", "attachment; filename=" + filename);
    Resource resource = new InputStreamResource(new FileInputStream(file));
    return ResponseEntity.ok()
            .headers(headers)
            .contentLength(file.length())
            .contentType(MediaType.parseMediaType("application/x-protobuf"))
            .body(resource);
}

As I said, I think what I am not able to get it right is the sourceLayer in the Javascript. So, below is a sample of the JSON that I used to generate the PBF with tippecanoe.

> {
"type": "FeatureCollection",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "FIPS_CODE": 8009, "PARCEL_APN": null, "TAXAPN": null, "SITE_HOUSE_NUMBER": null, "SITE_DIRECTION": null, "SITE_STREET_NAME": null, "SITE_MODE": null, "SITE_QUADRANT": null, "SITE_UNIT_PREFIX": null, "SITE_UNIT_NUMBER": null, "SITE_CITY": null, "SITE_STATE": null, "SITE_ZIP": null, "SITE_PLUS_4": null, "_X_COORD": -102.554234, "_Y_COORD": 37.057221, "ADDR_SCORE": null, "OWNER_NAME_1": null, "OWNER_NAME_2": null, "MAIL_HOUSE_NUMBER": null, "MAIL_DIRECTION": null, "MAIL_STREET_NAME": null, "MAIL_MODE": null, "MAIL_QUADRANT": null, "MAIL_CITY": null, "MAIL_STATE": null, "MAIL_ZIP": null, "MAIL_PLUS_4": null, "MAIL_UNIT_PREFIX": null, "MAIL_UNIT_NUMBER": null, "USE_CODE_MUNI_DESC": null, "USE_CODE_MUNI": null, "USE_CODE_STD_LPS": null, "USE_CODE_STD_DESC_LPS": null, "USE_CODE_STD_CTGR_DESC_LPS": null, "USE_CODE_STD_CTGR_LPS": null, "AGGR_ACREAGE": null, "AGGR_LOT_COUNT": null, "ZONING": null, "BLDG_NUMBER": null, "BUILDING_SQFT": null, "STORIES_NUMBER": null, "STORIES_NUMBER_DESC": null, "TOTAL_ROOMS": null, "UNITS_NUMBER": null, "PARKING_SPACES": null, "BLDG_CLASS": null, "BLDG_CLASS_DESC": null, "CONSTRUCTION_CODE": null, "CONSTRUCTION_CODE_DESC": null, "EXTERIOR_WALL_TYPE": null, "EXTERIOR_WALL_DESC": null, "ROOF_COVER_TYPE": null, "ROOF_COVER_DESC": null, "LOCATION_ID": "US_08_009_XXX5168237061", "PROPERTY_DMP_ID": null, "PARCEL_DMP_ID": "516823706_1", "GEOM": "POLYGON ((-102.56102308 37.05449825, -102.56102664000002 37.061756619999983, -102.54291585 37.061754539999974, -102.54291557 37.058128049999986, -102.55197089 37.05812792, -102.55197016 37.050873270000011, -102.56102013 37.05087081, -102.56102308 37.05449825))" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -102.56102308, 37.05449825 ], [ -102.561026640000023, 37.061756619999983 ], [ -102.54291585, 37.061754539999974 ], [ -102.54291557, 37.058128049999986 ], [ -102.55197089, 37.05812792 ], [ -102.55197016, 37.050873270000011 ], [ -102.56102013, 37.05087081 ], [ -102.56102308, 37.05449825 ] ] ] } },
{ "type": "Feature", "properties": { "FIPS_CODE": 8009, "PARCEL_APN": null, "TAXAPN": null, "SITE_HOUSE_NUMBER": null, "SITE_DIRECTION": null, "SITE_STREET_NAME": null, "SITE_MODE": null, "SITE_QUADRANT": null, "SITE_UNIT_PREFIX": null, "SITE_UNIT_NUMBER": null, "SITE_CITY": null, "SITE_STATE": null, "SITE_ZIP": null, "SITE_PLUS_4": null, "_X_COORD": -102.737556, "_Y_COORD": 37.156019, "ADDR_SCORE": null, "OWNER_NAME_1": null, "OWNER_NAME_2": null, "MAIL_HOUSE_NUMBER": null, "MAIL_DIRECTION": null, "MAIL_STREET_NAME": null, "MAIL_MODE": null, "MAIL_QUADRANT": null, "MAIL_CITY": null, "MAIL_STATE": null, "MAIL_ZIP": null, "MAIL_PLUS_4": null, "MAIL_UNIT_PREFIX": null, "MAIL_UNIT_NUMBER": null, "USE_CODE_MUNI_DESC": null, "USE_CODE_MUNI": null, "USE_CODE_STD_LPS": null, "USE_CODE_STD_DESC_LPS": null, "USE_CODE_STD_CTGR_DESC_LPS": null, "USE_CODE_STD_CTGR_LPS": null, "AGGR_ACREAGE": null, "AGGR_LOT_COUNT": null, "ZONING": null, "BLDG_NUMBER": null, "BUILDING_SQFT": null, "STORIES_NUMBER": null, "STORIES_NUMBER_DESC": null, "TOTAL_ROOMS": null, "UNITS_NUMBER": null, "PARKING_SPACES": null, "BLDG_CLASS": null, "BLDG_CLASS_DESC": null, "CONSTRUCTION_CODE": null, "CONSTRUCTION_CODE_DESC": null, "EXTERIOR_WALL_TYPE": null, "EXTERIOR_WALL_DESC": null, "ROOF_COVER_TYPE": null, "ROOF_COVER_DESC": null, "LOCATION_ID": "US_08_009_XXX5168237062", "PROPERTY_DMP_ID": null, "PARCEL_DMP_ID": "516823706_2", "GEOM": "POLYGON ((-102.7330126 37.148770959999979, -102.74207003 37.148767860000021, -102.74210194 37.16326582, -102.73304057000001 37.16326841, -102.7330126 37.148770959999979))" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -102.7330126, 37.148770959999979 ], [ -102.74207003, 37.148767860000021 ], [ -102.74210194, 37.16326582 ], [ -102.733040570000014, 37.16326841 ], [ -102.7330126, 37.148770959999979 ] ] ] } },
{ "type": "Feature", "properties": { "FIPS_CODE": 8009, "PARCEL_APN": null, "TAXAPN": null, "SITE_HOUSE_NUMBER": null, "SITE_DIRECTION": null, "SITE_STREET_NAME": null, "SITE_MODE": null, "SITE_QUADRANT": null, "SITE_UNIT_PREFIX": null, "SITE_UNIT_NUMBER": null, "SITE_CITY": null, "SITE_STATE": null, "SITE_ZIP": null, "SITE_PLUS_4": null, "_X_COORD": -102.81506, "_Y_COORD": 37.004085, "ADDR_SCORE": null, "OWNER_NAME_1": null, "OWNER_NAME_2": null, "MAIL_HOUSE_NUMBER": null, "MAIL_DIRECTION": null, "MAIL_STREET_NAME": null, "MAIL_MODE": null, "MAIL_QUADRANT": null, "MAIL_CITY": null, "MAIL_STATE": null, "MAIL_ZIP": null, "MAIL_PLUS_4": null, "MAIL_UNIT_PREFIX": null, "MAIL_UNIT_NUMBER": null, "USE_CODE_MUNI_DESC": null, "USE_CODE_MUNI": null, "USE_CODE_STD_LPS": null, "USE_CODE_STD_DESC_LPS": null, "USE_CODE_STD_CTGR_DESC_LPS": null, "USE_CODE_STD_CTGR_LPS": null, "AGGR_ACREAGE": null, "AGGR_LOT_COUNT": null, "ZONING": null, "BLDG_NUMBER": null, "BUILDING_SQFT": null, "STORIES_NUMBER": null, "STORIES_NUMBER_DESC": null, "TOTAL_ROOMS": null, "UNITS_NUMBER": null, "PARKING_SPACES": null, "BLDG_CLASS": null, "BLDG_CLASS_DESC": null, "CONSTRUCTION_CODE": null, "CONSTRUCTION_CODE_DESC": null, "EXTERIOR_WALL_TYPE": null, "EXTERIOR_WALL_DESC": null, "ROOF_COVER_TYPE": null, "ROOF_COVER_DESC": null, "LOCATION_ID": "US_08_009_XXX5168237063", "PROPERTY_DMP_ID": null, "PARCEL_DMP_ID": "516823706_3", "GEOM": "MULTIPOLYGON (((-102.8144019 37.000220300000024, -102.81440001 37.003712959999973, -102.80987063 37.0037188, -102.80987594 37.000216500000022, -102.80534993 37.000142429999983, -102.79628788000002 37.000114470000028, -102.79628782 36.999781059999975, -102.80082103000001 36.99990944000001, -102.80401110000003 37.000001709999985, -102.809876 37.000180469999968, -102.81285284 37.000269880000019, -102.8144019 37.000220300000024)), ((-102.81440001 37.003712959999973, -102.8152316 37.00371293, -102.81522985 37.00734288000001, -102.81439467000001 37.007342709999989, -102.81440001 37.003712959999973)), ((-102.82868814 37.01459485, -102.8324637 37.014591250000024, -102.83246369 37.014663750000011, -102.82794237000002 37.014704750000021, -102.82794456 37.010969900000021, -102.82861387000003 37.01096972, -102.82868814 37.01459485)), ((-102.83246474 37.00738149, -102.83298162 37.007378249999988, -102.8330751 37.010969139999986, -102.83246423000001 37.010968670000011, -102.83246474 37.00738149)), ((-102.84676076 37.01446194, -102.84676385 37.014609619999987, -102.84604829 37.01460972000001, -102.8460482 37.01447282, -102.84676076 37.01446194)), ((-102.83734745 37.01097245, -102.83699172000001 37.010976860000028, -102.83699172000001 37.010972190000018, -102.83734745 37.01097245)), ((-102.83760191 37.010972639999977, -102.83734745 37.01097245, -102.83760182 37.0109693, -102.83760191 37.010972639999977)))" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -102.8144019, 37.000220300000024 ], [ -102.81440001, 37.003712959999973 ], [ -102.80987063, 37.0037188 ], [ -102.80987594, 37.000216500000022 ], [ -102.80534993, 37.000142429999983 ], [ -102.796287880000023, 37.000114470000028 ], [ -102.79628782, 36.999781059999975 ], [ -102.80082103, 36.99990944000001 ], [ -102.804011100000025, 37.000001709999985 ], [ -102.809876, 37.000180469999968 ], [ -102.81285284, 37.000269880000019 ], [ -102.8144019, 37.000220300000024 ] ] ], [ [ [ -102.81440001, 37.003712959999973 ], [ -102.8152316, 37.00371293 ], [ -102.81522985, 37.00734288000001 ], [ -102.814394670000013, 37.007342709999989 ], [ -102.81440001, 37.003712959999973 ] ] ], [ [ [ -102.82868814, 37.01459485 ], [ -102.8324637, 37.014591250000024 ], [ -102.83246369, 37.014663750000011 ], [ -102.827942370000017, 37.014704750000021 ], [ -102.82794456, 37.010969900000021 ], [ -102.828613870000027, 37.01096972 ], [ -102.82868814, 37.01459485 ] ] ], [ [ [ -102.83246474, 37.00738149 ], [ -102.83298162, 37.007378249999988 ], [ -102.8330751, 37.010969139999986 ], [ -102.832464230000014, 37.010968670000011 ], [ -102.83246474, 37.00738149 ] ] ], [ [ [ -102.84676076, 37.01446194 ], [ -102.84676385, 37.014609619999987 ], [ -102.84604829, 37.01460972000001 ], [ -102.8460482, 37.01447282 ], [ -102.84676076, 37.01446194 ] ] ], [ [ [ -102.83734745, 37.01097245 ], [ -102.836991720000015, 37.010976860000028 ], [ -102.836991720000015, 37.010972190000018 ], [ -102.83734745, 37.01097245 ] ] ], [ [ [ -102.83760191, 37.010972639999977 ], [ -102.83734745, 37.01097245 ], [ -102.83760182, 37.0109693 ], [ -102.83760191, 37.010972639999977 ] ] ] ] } },

One more thing I have tried, in the browser console, I tried to import a pbf:

datasource = new atlas.source.VectorTileSource(null, { tiles: [http://127.0.0.1:8080/api/file/5/1/13.pbf'], maxZoom:12});

I get back an Object, and when I try to build the layer with:

var buildingLayer = new atlas.layer.PolygonLayer(datasource, null { sourceLayer: 'Feature', fillColor: 'red', fillOpacity: 0.7});

I get undefined.

2

There are 2 answers

0
msuzuki On BEST ANSWER

Ok, I've found the couple of mistakes I've made. Thanks to @rbrundritt for his suggestions, and to the points I had to investigate.

I used a different geojson polygon set (MS Building Footprint) to test generated tippecanoe files.

Before I was running something like:

tippecanoe --no-feature-limit --no-tile-size-limit --exclude-all --minimum-zoom=5 --maximum-zoom=g --output-to-directory "/home/markus/Documents/AssessorTiles" /home/markus/Documents/Assessor_Json/*.json

The --exclude-all was removing all the columns that had data classification for the polygons (geometry).

So, I ran the following with the MS Building Footprint:

tippecanoe --no-feature-limit --no-tile-size-limit --minimum-zoom=2 --no-tile-compression --maximum-zoom=g --output-to-directory "/home/markus/Documents/GA_MicrosoftBuilding" /home/markus/Documents/MS_Building_Footprints_GA_Only/*.json

Now, the MS Building Footprint was a modified one where I included an incremental counter for the each polygon and have the state added.

Here is the sample of the modified MS Building Footprint json:

    {
"type": "FeatureCollection",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "STATE": "GA", "BLDID": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -82.561175, 30.686596 ], [ -82.561175, 30.686414 ], [ -82.560928, 30.686414 ], [ -82.560928, 30.686596 ], [ -82.561175, 30.686596 ] ] ] } },
{ "type": "Feature", "properties": { "STATE": "GA", "BLDID": 2 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -82.570518, 30.683769 ], [ -82.570513, 30.683796 ], [ -82.570401, 30.68378 ], [ -82.570386, 30.683854 ], [ -82.570595, 30.683884 ], [ -82.570615, 30.683783 ], [ -82.570518, 30.683769 ] ] ] } },
{ "type": "Feature", "properties": { "STATE": "GA", "BLDID": 3 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -82.125603, 30.740081 ], [ -82.125472, 30.740081 ], [ -82.125472, 30.740141 ], [ -82.125603, 30.740141 ], [ -82.125603, 30.740081 ] ] ] } },
{ "type": "Feature", "properties": { "STATE": "GA", "BLDID": 4 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -82.075463, 30.737503 ], [ -82.075425, 30.737516 ], [ -82.075433, 30.737534 ], [ -82.075434, 30.737534 ], [ -82.075449, 30.737568 ], [ -82.075487, 30.737555 ], [ -82.075463, 30.737503 ] ] ] } },

The Key plays an important role in reading the PBFs back into azure maps javascript.

Here is the change I made to azure maps javascript:

                var buildingLayer = new atlas.layer.PolygonLayer(datasource, null, {
                sourceLayer: 'GA',
                fillColor: 'red',
                fillOpacity: 0.7
            });
            map.layers.add(buildingLayer, 'labels');

So, I used the state key, in this case 'GA' as a sourceLayer in the azure maps javascript.

Then woola, the vector tiles (PBFs) displays on the map browser.

That would be great if Microsoft could generate the polygons based on a more generic key. For now, the only solution I see is to add a generic key i.e: 'POLYGON' into the json and rebuild the PBFs.

2
rbrundritt On

Some things to try:

  • One possibility is the zoom range of data in the vector tiles and the max zoom setting you have on the source. Max zoom on the source specifies the closest zoom level the source data is available for. Looking at your polygons, they would be small, but visible at zoom level 12, when zoomed out a two or more levels though, they end up being really small.

  • Double check the settings you used to create your vector tiles and ensure you have them available at zoom levels 12 or more zoomed out (i.e. 11, 10...)

  • Check the network calls to verify if your vector tiles are being requested.

  • Double check the source layer ID in your vector tiles and make sure it is identical in the Polygon layer.

  • Zoom the map where you expect the data to be (south, east Colorado) and make sure not to zoom in too much, then try this line of code in the browsers console. It should retrieve all features in the datasource that are being rendered. map.map.querySourceFeatures(datasource.getId(), { sourceLayer: 'Feature'})