MapInfo Pro

 View Only

MapInfo Monday: Analysing and Querying Raster Data

  • 1.  MapInfo Monday: Analysing and Querying Raster Data

    Employee
    Posted 08-19-2024 05:01

    In last week's article, we presented our Geo Raster SDK 2.0 and walked through the benefits of raster.

    We created a raster from a polygon contour TAB file using the SDK and then showed the newly created MRR table it in MapInfo Pro and Spectrum Spatial. With Spatial Manager we walked through how to create a layer and custom style for the raster.

    This week we cover some of the analysis functions available in Spatial Analyst for end users to query raster data. We then walk through the raster operations which are exposed in MISQL and through Spectrum Spatial's feature REST API. As mentioned last week SQL based query of raster data is one of the more powerful capabilities of our Spatial APIs. It enables many of the above use cases to be automated in business processes that are improved by using raster data analysis and enrichment.

    Performing analysis using Spectrum Spatial Analyst.

    Here we take the styled raster that was created at the end of last week's article into Spatial Analyst and perform some basic raster analysis.

    There is an animated GIF at the end for the entire process and below we walk through each of the main steps.

    We start by showing the original layer and then browse and add the newly styled layer. Below is the new layer made visible and with the original one hidden. You will notice that the legend for the new layer now has the custom breaks with regular steps of 200 metres.

    Spatial Analyst lets users perform.

    • Point inspection - to get a grid cell value by clicking the map once.
    • Line inspection - by drawing a line and choosing to view map information. Both line statics and elevation profile are displayed.
    • Polygon inspection - by drawing a polygon and choosing to view map information.

    Next, we use the annotation tools to draw a line that cuts through Lundy Island and then across Dartmoor National Park. After completing the annotation, the map information option in the annotation menu lets us query any visible layers including any vector layers (if we had some) and raster layers.


    Below we see the map information for the raster along the line. Two sets of information are shown.

    • First, we have the profile of the raster data along the line shown as a chart and table. The table is scrollable and has 100 entries since Spatial Analyst requests 100 sample points to be created along the line for the profile. The data has the XY and elevation value of each sample point.
    • Second, we have the statistics along the line. The statistics are calculated for all grid cells that intersect the line.

    There is also an export menu (show expanded below) allowing the data to be exported to CSV.

    If the raster had multiple fields and bands, then the information display would repeat for each field and band combination.


    Next, we draw a polygon annotation and again use the Map Information menu to retrieve information that intersects the polygon.

    Below we see that we have the statistics for the polygon area. The maximum elevation is 250.1999 metres and the difference between max and min is 252 since the lowest elevation is negative. This data can also be exported to CSV.


    Here is an animated GIF of the entire process.


    Using the Spatial Feature Service to query the raster data.

    Below we walk through the Spatial Feature REST API which can be used to make SQL based requests to query a raster table.

    All the capabilities demonstrated above, and more are available using SQL within the Spectrum Spatial Feature REST Service. The available functions are listed here and the API we will use is the searchBySQL which is documented here.

    Using SQL to query raster tables makes automating the process and integrating it into wider workflows possible.

    Describing a raster table

    Often a first step is to describe the raster table. This returns information about the projection, the extents, the number of fields and bands and their name and type.

    Below we see the raster is in British National Grid projection (epsg:27700) and comprises 13312 x 25600 grid cells. The table has one field representing a continuous raster, called "Field1" and one band within that field. If there were multiple fields and bands, they would all be listed in the array.

    Request

    http://Localhost:8080/rest/Spatial/FeatureService/tables/RasterMapInfoMonday/BritishDEM_Polygons_Rasterize/metadata.json

    Response

    {
        "Metadata": [
            {
                "name": "MI_RASTER",
                "type": "Raster",
                "crs": {
                    "type": "name",
                    "properties": {
                        "name": "epsg:27700"
                    }
                },
                "bbox": [
                    2.728484105318782E-10,
                    4.6566128730773926E-10,
                    665599.9999999997,
                    1279999.9999999993
                ]
            },
            {
                "name": "MI_Style",
                "type": "Style"
            }
        ],
        "TableMetadata": {
            "supportsInsert": false,
            "supportsDelete": false,
            "supportsUpdate": false,
            "KeyDefinition": {
                "type": "Implicit",
                "attributes": [
                    "MapInfo_ID"
                ]
            },
            "rasterMetadata": {
                "width": 13312,
                "height": 25600,
                "supportsGridCellValue": true,
                "fields": [
                    {
                        "name": "Field1",
                        "type": "Continuous",
                        "bands": [
                            {
                                "name": "Value",
                                "type": "Concrete",
                                "dataType": "Double",
                                "mapInfoUnits": "Meters"
                            }
                        ]
                    }
                ]
            }
        }
    }

    Grid Cell value at a point.

    We can then make a request for the grid cell value at a location. Since the raster is in EPSG:27700 we are using British National Grid co-ordinates to specify the XY of the point in the request. The point below is in Penzance. We are querying the field 0 and band 0, also aliasing the Band name to return something more meaningful like "Elevation". The response shows the elevation as being 47.2 metres.

    Request

    http://Localhost:8080/rest/Spatial/FeatureService/tables/features.json?q=SELECT MI_GridValueAt("MI_RASTER", MI_POINT(146570,30497,'EPSG:27700'),0,0) as Elevation from  "/RasterMapInfoMonday/BritishDEM_Polygons_Rasterize"

    Response

    {
        "type": "FeatureCollection",
        "features": [
            {
                "type": "Feature",
                "properties": {
                    "Elevation": 47.20000076293945
                },
                "geometry": null,
                "id": 1
            }
        ],
        "Metadata": [
            {
                "name": "Elevation",
                "type": "Double"
            }
        ]
    }

    It is also possible to specify multiple bands if your MRR has more than one band. Here is a more complex query with 2 fields and a total of 8 bands specified including alias names for the response field values.

    http://localhost:8080/rest/Spatial/FeatureService/tables/features.json?q=SELECT
    MI_GridValueAt ("MI_RASTER", MI_POINT(-8237000,4970000,'EPSG:3857'),0,0) as SignalField_SignalBand,
    MI_GridValueAt ("MI_RASTER", MI_POINT(-8237000,4970000,'EPSG:3857'),1,0) as SectorField_SectorBand,
    MI_GridValueAt ("MI_RASTER", MI_POINT(-8237000,4970000,'EPSG:3857'),1,1) as SectorField_Index,
    MI_GridValueAt ("MI_RASTER", MI_POINT(-8237000,4970000,'EPSG:3857'),1,2) as SectorField_Label,
    MI_GridValueAt ("MI_RASTER", MI_POINT(-8237000,4970000,'EPSG:3857'),1,3) as SectorField_Color,
    MI_GridValueAt ("MI_RASTER", MI_POINT(-8237000,4970000,'EPSG:3857'),1,4) as SectorField_BLUE,
    MI_GridValueAt ("MI_RASTER", MI_POINT(-8237000,4970000,'EPSG:3857'),1,5) as SectorField_GREEN,
    MI_GridValueAt ("MI_RASTER", MI_POINT(-8237000,4970000,'EPSG:3857'),1,6) as SectorField_RED
    from "/RasterMapInfoMonday/USA_Cell_Coverage"

    Region statistics

    Here we make a request for region statistics. The polygon is passed as WKT but can also be sent as GeoJSON. The polygon is similar to (but not exactly the same) the one used for Lands' End in the Spatial Analyst example above. The statistics are returned in an escaped JSON format in the "MI_GridRegionStats_MI_RASTER_F" property.

    Request

    http://Localhost:8080/rest/Spatial/FeatureService/tables/features.json?q=SELECT MI_GridRegionStats("MI_RASTER",FromWKT('POLYGON ((186925 10521, 138747 13450, 127029 39817, 150466 50560, 189529 22890, 186925 10521))', 'EPSG:27700'),0,0) from  "/RasterMapInfoMonday/BritishDEM_Polygons_Rasterize

    Response

    {
        "type": "FeatureCollection",
        "features": [
            {
                "type": "Feature",
                "properties": {
                    "MI_GridRegionStats_MI_RASTER_F": "[{\"field\":0,\"band\":0,\"min\":-1.7999999523162842,\"max\":250.1999969482422,\"range\":252.0,\"mean\":44.6939131555061,\"median\":-1.7999999523162842,\"mode\":-1.7999999523162842,\"std_dev\":58.052161250477475,\"low_quart\":-1.7999999523162842,\"up_quart\":83.19999694824219,\"quart_range\":84.99999690055847,\"node_sum\":2.4135651676149562E7,\"pct_num_cells\":13.492138503312,\"coef_of_var\":1.2988829384550251,\"num_cells\":624245.0,\"num_null_cells\":84224.0}]"
                },
                "geometry": null,
                "id": 1
            }
        ],
        "Metadata": [
            {
                "name": "MI_GridRegionStats_MI_RASTER_F",
                "type": "String"
            }
        ]
    }

    Line statistics.

    The line statistics request is very similar to the region statistics. The line runs broadly along the same path as we showed in Spatial Analyst above across Dartmoor.

    Request

    http://Localhost:8080/rest/Spatial/FeatureService/tables/features.json?q=select MI_GridLineStats("MI_RASTER",FromWKT('LINESTRING (211562 152183, 236953 96845, 278294 66897, 294570 36298)', 'EPSG:27700'),0,0) from "/RasterMapInfoMonday/BritishDEM_Polygons_Rasterize"

    Response

    {
        "type": "FeatureCollection",
        "features": [
            {
                "type": "Feature",
                "properties": {
                    "MI_GridLineStats_MI_RASTER_Fro": "[{\"field\":0,\"band\":0,\"min\":-2.799999952316284,\"max\":555.2000122070312,\"range\":558.0000121593475,\"mean\":148.4500006735325,\"start_val\":\"Null\",\"middle_val\":\"131.199997\",\"end_val\":\"Null\"}]"
                },
                "geometry": null,
                "id": 1
            }
        ],
        "Metadata": [
            {
                "name": "MI_GridLineStats_MI_RASTER_Fro",
                "type": "String"
            }
        ]
    }

    Line Profile

    Finally, we have the elevation along a line. Here we also need to pass a value of how many steps we need along the line. 

    Whilst Analyst defaults to requesting 100 steps, for presentation purposes, we pass only 4 steps for field 0 and band 0 below, so that the response is smaller.

    Note that there is no upper limit to how many steps can be requested. If too many are specified, the API defaults to the resolution of the raster which in this case would be one step every 50 metres.

    The response is in an escaped JSON form that has the X and Y value of each step along with the raster value. The status "valid" means the point is inside the MRR grid and has a value. Other status values can be "off-grid" or "Null" where the cell is inside the grid but has no value.

    Request

    http://Localhost:8080/rest/Spatial/FeatureService/tables/features.json?q=SELECT MI_GridLineProfile("MI_RASTER",FromWKT('LINESTRING (211562 152183, 236953 96845, 278294 66897, 294570 36298)', 'EPSG:27700'),4,0,0) from "/RasterMapInfoMonday/BritishDEM_Polygons_Rasterize"

    Response

    {
        "type": "FeatureCollection",
        "features": [
            {
                "type": "Feature",
                "properties": {
                    "MI_GridLineProfile_MI_RASTER_F": "[{\"field\":0,\"band\":0,\"cross_sections\":[{\"distance\":0.0,\"value\":0.0,\"x\":211561.99781554582,\"y\":152182.99732143723,\"status\":\"NULL\"},{\"distance\":48864.053678407836,\"value\":138.1999969482422,\"x\":231939.839473206,\"y\":107770.8501662574,\"status\":\"VALID\"},{\"distance\":97728.10735681567,\"value\":303.20001220703125,\"x\":266789.78340327245,\"y\":75230.81465448644,\"status\":\"VALID\"},{\"distance\":146592.16103522351,\"value\":0.0,\"x\":294569.9996921978,\"y\":36297.999056971064,\"status\":\"NULL\"}]}]"
                },
                "geometry": null,
                "id": 1
            }
        ],
        "Metadata": [
            {
                "name": "MI_GridLineProfile_MI_RASTER_F",
                "type": "String"
            }
        ]
    }

    Here is a parsed copy of the JSON response. You can see that the distance of the step from the start of the line is included along with the X and Y and the band value. The first and last point are in the sea hence have zero elevation.

    {
        "field": 0,
        "band": 0,
        "cross_sections": [
            {
                "distance": 0.0,
                "value": 0.0,
                "x": 211561.99781554583,
                "y": 152182.99732143724,
                "status": "NULL"
            },
            {
                "distance": 48864.053678407836,
                "value": 138.1999969482422,
                "x": 231939.839473206,
                "y": 107770.8501662574,
                "status": "VALID"
            },
            {
                "distance": 97728.10735681567,
                "value": 303.20001220703127,
                "x": 266789.78340327247,
                "y": 75230.81465448645,
                "status": "VALID"
            },
            {
                "distance": 146592.16103522352,
                "value": 0.0,
                "x": 294569.9996921978,
                "y": 36297.999056971064,
                "status": "NULL"
            }
        ]
    }

    We hope you enjoyed this deep dive into raster. Below are some further resources.

    • Background on raster support and MRR. link
    • Raster SDK 2.0 release notes link
    • Raster SDK 2.0 guide.  link
    • Raster SDK 2.0 sample project. link
    • Raster functions available in the Private Spatial APIs. The same are also available in Spectrum Spatial.  link
    • Spatial Analyst documentation on raster information.  link
    • Spatial Manager documentation on raster styling.  link


    ------------------------------
    Mustafa Ismail
    Precisely
    ------------------------------