Example of use

[1]:
import LMIPy
[2]:
LMIPy.__version__
[2]:
'0.4.0'

Collection objects: Searching

If you don’t know what data you are interested in advance, you can search by keywords and return a list of objects.

[15]:
c = LMIPy.Collection('tree cover', object_type=['layer','dataset'], app=['gfw'], limit=10)
[16]:
c
[16]:
0. Brazil Land Cover
Data source wms 🗺Dataset in GFW.
Last Modified: 2019-06-20T15:12:04.531Z
Connector: wms | Published: False

1. Brazil Land Cover 1985-2017
GEE asset: projects/wri-datalab/mapbiomas_collection31_integration_v1 🗺Dataset in GFW.
Last Modified: 2019-06-20T15:05:30.770Z
Connector: gee | Published: True

2. Brazil land cover - 2000-2016
Data source leaflet 🗺Layer in GFW.
Last Modified: 2018-11-21T14:03:00.520Z
Connector: leaflet | Published: True

3. Brazil land cover 1985
GEE asset: None 🗺Layer in GFW.
Last Modified: 2019-06-20T15:06:45.809Z
Connector: gee | Published: True

4. Brazil land cover 1986
GEE asset: None 🗺Layer in GFW.
Last Modified: 2019-06-20T15:06:44.081Z
Connector: gee | Published: True

5. Brazil land cover 1987
GEE asset: None 🗺Layer in GFW.
Last Modified: 2019-06-20T15:06:42.315Z
Connector: gee | Published: True

6. Brazil land cover 1988
GEE asset: None 🗺Layer in GFW.
Last Modified: 2019-06-20T15:06:40.484Z
Connector: gee | Published: True

7. Brazil land cover 1989
GEE asset: None 🗺Layer in GFW.
Last Modified: 2019-06-20T15:06:38.428Z
Connector: gee | Published: True

8. Brazil land cover 1990
GEE asset: None 🗺Layer in GFW.
Last Modified: 2019-06-20T15:06:36.447Z
Connector: gee | Published: True

9. Brazil land cover 1991
GEE asset: None 🗺Layer in GFW.
Last Modified: 2019-06-20T15:06:34.470Z
Connector: gee | Published: True

Searching can be restricted with keyword arguments to specifically search types of items, applications, and more. If you want to render those items, you will need to do the following.

You can access items from a collection using subscripts, slices and more. Note that slicing, or selecting by element instantiates the Layer, Table, or Dataset object.

[17]:
c[0:3]
[17]:
[Dataset f3fc0f1e-aa26-49b6-8741-45df2eea9ac2 Brazil Land Cover,
 Dataset b3bfa285-ab43-4562-b2e0-0ab3e92c59e3 Brazil Land Cover 1985-2017,
 Layer 220080ec-1641-489c-96c4-4885ed618bf3 Brazil land cover - 2000-2016]
[18]:
c[-1]
[18]:
Brazil land cover 1991
GEE asset: None 🗺Layer in GFW.
Last Modified: 2019-06-20T15:06:34.470Z
Connector: gee | Published: True
[19]:
l = c[-1]
l.map()
[19]:

Custom collections

As Collections are simply named groups of API entities (Layers, Datasets, or Widgets) you can create and manage your own given you have authrosation to do so.

Note that you can only view and manage your own custom Collections. See RW-API docs for more.

Creating a new Collection

Specify a list of valid objects, name, and application.

The resources list may be a list of LMIPy objects or a list of dicts specifying the type and id of each resource.

[8]:
layer_1 = LMIPy.Layer('0cba3c4f-2d3b-4fb1-8c93-c951dc1da84b')
layer_2 = LMIPy.Layer('d5b0a81b-70cd-4f33-860a-34a3c17867f3')
[9]:
# List of LMIPy objects
resource_list = [
    layer_1,
    layer_2
]
[12]:
c = LMIPy.Collection(
    attributes={
        'resources': resource_list,
        'name': 'Example Collection 1',
        'application': 'gfw'
}, token=<TOKEN>)
https://api.resourcewatch.org/v1/collection/5f5b5e7e055805001a08ee5c
[13]:
# Collection resources
c
[13]:
0. Tree cover - 2000
Data source tilelayer | Layer in GFW.
Last Modified: 2020-08-12T17:41:07.987Z
Fields | Connector: tilelayer | Published: True

1. USA Land Cover - 2016
GEE asset: None | Layer in GFW.
Last Modified: 2020-08-12T17:39:32.936Z
Fields | Connector: gee | Published: True
[15]:
# Collection attributes
c.attributes
[15]:
{'application': 'gfw',
 'name': 'Example Collection 1',
 'ownerId': '59db4eace9c1380001d6e4c3',
 'resources': [{'attributes': {},
   'id': '0cba3c4f-2d3b-4fb1-8c93-c951dc1da84b',
   'server': 'https://api.resourcewatch.org',
   'type': 'Layer'},
  {'attributes': {},
   'id': 'd5b0a81b-70cd-4f33-860a-34a3c17867f3',
   'server': 'https://api.resourcewatch.org',
   'type': 'Layer'}]}

Getting Collections by ID

Requires authorisation.

[ ]:
c = LMIPy.Collection(id_hash='5f5b5e7e055805001a08ee5c', token=<TOKEN>)
[24]:
c.attributes['name']
[24]:
'Example Collection 1'
[22]:
c
[22]:
0. Tree cover - 2000
Data source tilelayer | Layer in GFW.
Last Modified: 2020-08-12T17:41:07.987Z
Fields | Connector: tilelayer | Published: True

1. USA Land Cover - 2016
GEE asset: None | Layer in GFW.
Last Modified: 2020-08-12T17:39:32.936Z
Fields | Connector: gee | Published: True

Adding further resources

You may also add more resources to the Collection

[17]:
dataset_1 = LMIPy.Dataset('c92b6411-f0e5-4606-bbd9-138e40e50eb8')
[25]:
updated_c = c.add_resources(resources=[dataset_1], token=<TOKEN>)
Dataset c92b6411-f0e5-4606-bbd9-138e40e50eb8 added to Collection 5f5b5e7e055805001a08ee5c.
[26]:
updated_c.attributes['name']
[26]:
'Example Collection 1'
[27]:
updated_c
[27]:
0. Tree cover - 2000
Data source tilelayer | Layer in GFW.
Last Modified: 2020-08-12T17:41:07.987Z
Fields | Connector: tilelayer | Published: True

1. USA Land Cover - 2016
GEE asset: None | Layer in GFW.
Last Modified: 2020-08-12T17:39:32.936Z
Fields | Connector: gee | Published: True

2. Tree cover (LM v3)
GEE asset: UMD/hansen/global_forest_change_2013 | Dataset in GFW.
Last Modified: 2020-03-31T08:31:42.644Z
Fields | Connector: gee | Published: True

Deleting custom collection

[4]:
c = LMIPy.Collection(id_hash='5f5b5e7e055805001a08ee5c', token=<TOKEN>)
[6]:
c.delete(token=<TOKEN)
Delete Collection Example Collection 1 with id=5f5b5e7e055805001a08ee5c?
> y/n
y
https://api.resourcewatch.org/collection/5f5b5e7e055805001a08ee5c
Deletion successful!
[8]:
try:
    c = LMIPy.Collection(id_hash='5f5b5e7e055805001a08ee5c', token=<TOKEN>)
except:
    print(f"Collection does not exist.")
Collection does not exist.

Dataset objects

Using known id’s you can instantiate a dataset object directly.

[3]:
ds = LMIPy.Dataset('044f4af8-be72-4999-b7dd-13434fc4a394')
ds
[3]:
Tree cover
GEE asset: UMD/hansen/global_forest_change_2013 🗺Dataset in GFW, GFW-PRO.
Last Modified: 2018-11-21T13:55:01.210Z
Connector: gee | Published: True

You can access the attributes of a dataset.

[4]:
ds.attributes
[4]:
{'application': ['gfw', 'gfw-pro'],
 'attributesPath': None,
 'blockchain': {},
 'clonedHost': {},
 'connectorType': 'rest',
 'connectorUrl': None,
 'createdAt': '2019-09-19T15:25:32.570Z',
 'dataLastUpdated': None,
 'dataPath': None,
 'env': 'production',
 'errorMessage': '[Automatic Validation] ConnectorFailed -> Invalid Dataset',
 'geoInfo': False,
 'layerRelevantProps': [],
 'legend': {'binary': [],
  'boolean': [],
  'byte': [],
  'country': [],
  'date': [],
  'double': [],
  'float': [],
  'half_float': [],
  'integer': [],
  'keyword': [],
  'nested': [],
  'region': [],
  'scaled_float': [],
  'short': [],
  'text': []},
 'mainDateField': None,
 'name': 'Tree cover',
 'overwrite': False,
 'protected': True,
 'provider': 'gee',
 'published': True,
 'slug': 'Tree-cover',
 'sources': [],
 'status': 'saved',
 'subtitle': None,
 'tableName': 'UMD/hansen/global_forest_change_2013',
 'taskId': None,
 'type': None,
 'updatedAt': '2018-11-21T13:55:01.210Z',
 'userId': '596cde70824315350dd0f116',
 'verified': False,
 'widget': [],
 'widgetRelevantProps': []}

You can also access the metadata and vocabularies if they exist.

[5]:
ds.vocabulary[0].attributes
[5]:
{'application': 'gfw',
 'name': 'categoryTab',
 'resource': {'id': '044f4af8-be72-4999-b7dd-13434fc4a394', 'type': 'dataset'},
 'tags': ['landCover']}
[6]:
ds.metadata[0].attributes
[6]:
{'application': 'gfw',
 'createdAt': '2018-08-03T10:17:06.249Z',
 'dataset': '044f4af8-be72-4999-b7dd-13434fc4a394',
 'info': {'citation': '2000/2010, Hansen/UMD/Google/USGS/NASA',
  'color': '#a0c746',
  'description': 'Identifies areas of tree cover.',
  'isSelectorLayer': True,
  'name': 'Tree cover'},
 'language': 'en',
 'resource': {'id': '044f4af8-be72-4999-b7dd-13434fc4a394', 'type': 'dataset'},
 'status': 'published',
 'updatedAt': '2018-11-06T15:57:49.716Z'}

Queries on Datasets

Datasets can be queried via SQL, with a table returned. Currently this is only supported for Carto-type data:

[7]:
d = LMIPy.Dataset(id_hash='bd5d7924-611e-4302-9185-8054acb0b44b')
d
[7]:
Global Mangrove Watch
Carto table: mangroves_1996 🗺Dataset in GFW.
Last Modified: 2018-11-21T14:03:09.440Z
Connector: cartodb | Published: True
[8]:
d.query('SELECT fid, ST_ASGEOJSON(the_geom_webmercator) FROM data LIMIT 5')
[8]:
fid st_asgeojson
0 0 {"type":"MultiPolygon","coordinates":[[[[-6998...
1 62 {"type":"MultiPolygon","coordinates":[[[[-6927...
2 343 {"type":"MultiPolygon","coordinates":[[[[-6996...
3 402 {"type":"MultiPolygon","coordinates":[[[[-7004...
4 682 {"type":"MultiPolygon","coordinates":[[[[-6937...

Layer Objects

Similarly, you can also instantiate a Layer object.

[9]:
ly = LMIPy.Layer(id_hash='dc6f6dd2-0718-4e41-81d2-109866bb9edd')
ly
[9]:
Tree cover 2000
Data source leaflet 🗺Layer in GFW-PRO.
Last Modified: 2018-07-19T08:33:23.226Z
Connector: leaflet | Published: True

Layers can be visulized if appropriate via a call to Layer().map()

[10]:
ly.map()
[10]:

Tables

Tables are subclasses of Dataset objects. They are document datasets which can be instantiated and queried returning a dataframe object.

[11]:
t = LMIPy.Table(id_hash='86c7135a-855d-4f1b-9d67-f545a93281b3')
t
[11]:
AQ City Days (DUMMY, NOT FOR PUBLISH)
CSV Table: index_86c7135a855d4f1b9d67f545a93281b3_1520615201102 🗺Table in RW.
Last Modified: 2018-03-09T17:06:52.555Z
Connector: csv | Published: False
[12]:
df = t.head(3)
df
[12]:
City Number_of_Days _id
0 Beijing 14 AWILuq8j8Jqrt1rp-STQ
1 San Francisco 10 AWILuq8j8Jqrt1rp-STT
2 Manama 7 AWILuq8j8Jqrt1rp-STU

Queries to tables are returned in geopandas dataframe format.

[13]:
type(df)
[13]:
geopandas.geodataframe.GeoDataFrame
[14]:
t.query("SELECT * from data where City = 'San Francisco'")
[14]:
City Number_of_Days _id
0 San Francisco 10 AWILuq8j8Jqrt1rp-STT

Create a Geometry object

Often you will need to perform some kind of intersect analysis between data held in datasets and tables and a geometry. We will now show you multiple ways to create your geometry objects.

From an ID

Vizzuality’s API holds geometry objects as a Geostore item. Geostore items are accessed by an id-hash. If you know the hash of your object already you can simply call a geometry like so:

[20]:
g = LMIPy.Geometry(id_hash='e8b6f974bcab5aefccd121654860be06')
g

Geometry attributes

The attributes can be accessed as a dictionary.

[21]:
g.attributes
[21]:
{'areaHa': 16931274.241571266,
 'bbox': [-49.6113281249728,
  -9.77447583284213,
  -44.513671874972,
  -4.19348629349041],
 'geojson': {'crs': {},
  'features': [{'geometry': {'coordinates': [[[-47.8535156249717,
        -4.19348629349041],
       [-49.6113281249728, -9.77447583284213],
       [-44.513671874972, -9.25438811084709],
       [-47.8535156249717, -4.19348629349041]]],
     'type': 'Polygon'},
    'properties': None,
    'type': 'Feature'}],
  'type': 'FeatureCollection'},
 'hash': 'e8b6f974bcab5aefccd121654860be06',
 'info': {'use': {}},
 'lock': False,
 'provider': {}}

Geometry as a Table

Table method returns a dataframe of the geometry object. Map will add a Folium map with the geomerty rendered.

[22]:
g.table()
[22]:
areaHa bbox geometry id use
0 1.693127e+07 [-49.6113281249728, -9.77447583284213, -44.513... POLYGON ((-47.8535156249717 -4.19348629349041,... e8b6f974bcab5aefccd121654860be06 {}

Mapping the Geometry

Calling .map() will create a Folium map with the geomerty rendered.

[23]:
g.map()
[23]:

From Geojson - Points

You can create an object as you need on the fly from geojson. The act of creating an object will also register it to a Geostore service of your choice (locally, or on a remote server). You can create a geometry object from geojson Points and MultiPoints type data as follows:

[24]:
atts = {'geojson': {'type': 'FeatureCollection',
  'features': [{'type': 'Feature',
    'properties': {},
    'geometry': {'type': 'MultiPoint', 'coordinates': [[-4.29, 39.1097]]}}]}}

point = LMIPy.Geometry(attributes=atts, server='https://production-api.globalforestwatch.org')
point

From Geojson - Polygons

You can create an object as you need on the fly from geojson. The act of creating an object will also register it to a Geostore service of your choice (locally, or on a remote server). You can create a geometry object from Geojson Polygon and Multipolygon type data as follows:

[25]:
atts={'geojson': {'type': 'FeatureCollection',
  'features': [{'type': 'Feature',
    'properties': {},
    'geometry': {'type': 'Polygon',
     'coordinates': [[[82.265625, 32.84267363195431],
       [77.34374999999999, 27.059125784374068],
       [85.4296875, 22.268764039073968],
       [90.3515625, 28.304380682962783],
       [87.5390625, 32.54681317351514],
       [82.265625, 32.84267363195431]]]}}]}}


g1 = LMIPy.Geometry(attributes=atts)
g1
[26]:
g1.map()
[26]:

From a Shapely object

Shapely objects are at the root of popular python geolibraries such as Geopandas. We can recieve those geometry objects and create a Geometry object (simultaneously registering it in a Vizzuality Geostore server).

[27]:
import geopandas as gpd
[28]:
%%writefile ./sample.geojson
{"features":[{"properties":null,"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-43.1343734264374,-8.07358087603511],[-43.1327533721924,-8.08277985402466],[-43.1298887729645,-8.08181322762719],[-43.1103515625,-8.07815914647929],[-43.1094932556152,-8.07799981079283],[-43.1094932556152,-8.09641859926744],[-43.1103515625,-8.09645046495416],[-43.1187307834625,-8.0967372560211],[-43.1186878681183,-8.10273857778317],[-43.1186771392822,-8.10358831522616],[-43.1476235389709,-8.10358831522616],[-43.1477630138397,-8.10273857778317],[-43.1505310535431,-8.08645513764317],[-43.1517112255096,-8.08057041885644],[-43.1439757347107,-8.0795931648273],[-43.1448876857758,-8.07574785969913],[-43.1343734264374,-8.07358087603511]]]}}],"crs":{},"type":"FeatureCollection"}
Overwriting ./sample.geojson
[29]:
df = gpd.read_file('./sample.geojson')
df
[29]:
geometry
0 POLYGON ((-43.1343734264374 -8.073580876035111...
[30]:
s = df.geometry[0]
print(f"Hello! 👋 I am a {type(s)}")
s
Hello! 👋 I am a <class 'shapely.geometry.polygon.Polygon'>
[30]:
_images/quickstart_69_1.svg
[31]:
g = LMIPy.Geometry(s=s)
[32]:
g

From Political Boundaries

We are able to return political boundaries (based on GADM data) using ISO, admin keys, down to admin-2 level. These should be passed in a dictionary to params. GADM 3.6 is currently used by default.

[33]:
params={
    'iso': 'BRA',
    'adm1': None,
    'adm2': None
}
admin = LMIPy.Geometry(parameters=params)
admin.map()
[33]:

From an admin geometry with an older GADM version

Alternatively, you can specify a different gadm version.

[34]:

params={
    'iso': 'BRA',
    'adm1': 1,
    'adm2': 1,
    'gadm': '2.7'
}
admin_v1 = LMIPy.Geometry(parameters=params)
admin_v1.map()
[34]:

From a Carto table and index

You can also return geometries directly from a Carto table (under the public WRI-01 account) using the table name and cartodb_id.

[35]:
params={
    'table': 'gfw_logging',
    'id': 123
}
row_geom = LMIPy.Geometry(parameters=params)
row_geom.map()
[35]:

Describe a Geometry

Return a title and textual description of a geometry in any language.

[36]:
g.map()
[36]:
[37]:
%%time
g.describe()
Title: Area in Microrregião de São Raimundo Nonato, Piauí, Brazil
Description: The regions habitat is comprised of Caatinga. This region has no Intact Forest. The area has a predominantly equatorial climate with dry winters. It is part of the Tropical and Subtropical Dry Broadleaf Forests biome. The location is predominantly land area. Area of 1.15kha located in a lowland area.
CPU times: user 26.1 ms, sys: 2.18 ms, total: 28.2 ms
Wall time: 1.49 s
[38]:
g.describe(lang='es') # same description but this time in Spanish
Title: Área en la Microrregião de São Raimundo Nonato, Piauí, Brasil
Description: El hábitat regiones se compone de Caatinga. Esta región no tiene bosque intacto. La zona tiene un clima predominantemente ecuatorial, con inviernos secos. Es parte del bioma secos tropicales y subtropicales de hoja ancha bosques. La ubicación es predominantemente la superficie terrestre. Área de 1.15kha situado en una zona de tierras bajas.

Second example of geodescriber, but considering a larger area.

[39]:
atts={'geojson': {'type': 'FeatureCollection',
  'features': [{'type': 'Feature',
    'properties': {},
    'geometry': {'type': 'Polygon',
     'coordinates': [[[82.265625, 32.84267363195431],
       [77.34374999999999, 27.059125784374068],
       [85.4296875, 22.268764039073968],
       [90.3515625, 28.304380682962783],
       [87.5390625, 32.54681317351514],
       [82.265625, 32.84267363195431]]]}}]}}


g2 = LMIPy.Geometry(attributes=atts)
g2
[40]:
g2.map()
[40]:
[41]:
g2.describe()
Title: Area near Nepal, Asia
Description: The region is made up of different habitats, including Central Tibetan Plateau alpine steppe, and Upper Gangetic Plains moist deciduous forests. This region contains some Intact Forest. The most common environmental conditions of the area are polar tundra climate. The region is made up of several types of biomes, including Montane Grasslands and Shrublands, and Tropical and Subtropical Moist Broadleaf Forests. The location is predominantly land area. Area of 89.13Mha located in a mix of lowland and mountainous areas.

After running describe, the results are avaiable as geometry.description

[42]:
g2.description
[42]:
{'description': 'The region is made up of different habitats, including Central Tibetan Plateau alpine steppe, and Upper Gangetic Plains moist deciduous forests. This region contains some Intact Forest. The most common environmental conditions of the area are polar tundra climate. The region is made up of several types of biomes, including Montane Grasslands and Shrublands, and Tropical and Subtropical Moist Broadleaf Forests. The location is predominantly land area. Area of 89.13Mha located in a mix of lowland and mountainous areas.',
 'lang': 'en',
 'title': 'Area near Nepal, Asia'}

Add a satellite image to the map of your Geometry

Single images

The Geometry().map() method supports the return of recent satellite imagery. We calculate the centroid of your geometry and return the best, most recent image that intersects with that point. This is simply to provide context to your geometry. For point geometries we return the best cloud-free image we can find within a specified date range.

[43]:
# Create a point

atts = {'geojson': {'type': 'FeatureCollection',
  'features': [{'type': 'Feature',
    'properties': {},
    'geometry': {'type': 'MultiPoint', 'coordinates': [[-4.29, 39.1097]]}}]}}

point = LMIPy.Geometry(attributes=atts, server='https://production-api.globalforestwatch.org')
point
[44]:
point.map(image=True, start='2018-01-01', end='2018-12-31')
[44]:

Composite images

Whereas for polygon-type geoms we return a cloud-free composite image clipped to your geometry.

[45]:
# Create a polygon

atts={'geojson': {'type': 'FeatureCollection',
  'features': [{'type': 'Feature',
    'properties': {},
    'geometry': {'type': 'Polygon',
     'coordinates': [[[-0.87890625, 43.329173667843904],
       [-1.6149902343749998, 42.75104599038353],
       [-1.1865234375, 42.35854391749705],
       [-0.6427001953125, 42.755079545072135],
       [-0.45043945312499994, 42.9524020856897],
       [-0.87890625, 43.329173667843904]]]}}]}}

g1 = LMIPy.Geometry(attributes=atts, server='https://production-api.globalforestwatch.org')
g1
[46]:
g1.map(image=True, instrument='sentinel')
[46]:

Display a Layer and Geometry together

[47]:
atts={'geojson': {'type': 'FeatureCollection',
  'features': [{'type': 'Feature',
    'properties': {},
    'geometry': {'type': 'Polygon',
     'coordinates': [[[-48.131103515625, -0.6001172008725418],
       [-48.13934326171875, -0.9791088369866402],
       [-47.86331176757812, -0.9818550168696459],
       [-47.616119384765625, -0.8459165322899671],
       [-47.50213623046875, -0.7182123915862891],
       [-47.51861572265625, -0.5287095375108173],
       [-47.882537841796875, -0.5163504323777461],
       [-48.131103515625, -0.6001172008725418]]]}}]}}
g = LMIPy.Geometry(attributes=atts)
l = LMIPy.Layer(id_hash='3ef039ba-29c7-459d-aadd-66d6e6cc8f5e')
l
[47]:
Mangrove biomass density
GEE asset: None 🗺Layer in GFW.
Last Modified: 2019-06-11T20:36:06.441Z
Connector: gee | Published: True
[48]:
l.map(geometry=g)
[48]:

Intersecting Raster data with Geometry objects

Any Layer or Dataset objects based on an Earth Engine raster can be intersected with LMIPy.Geometry objects.

Dataset Intersections

In the first instance we show the result of an intersect between a geometry roughly bounding London, and a Population density layer.

[49]:
atts={'geojson': {'type': 'FeatureCollection',
  'features': [{'type': 'Feature',
    'properties': {},
    'geometry': {'type': 'Polygon',
     'coordinates': [
          [
            [
              -0.5712890625,
              51.28940590271679
            ],
            [
              0.48339843749999994,
              51.28940590271679
            ],
            [
              0.48339843749999994,
              51.781435604431195
            ],
            [
              -0.5712890625,
              51.781435604431195
            ],
            [
              -0.5712890625,
              51.28940590271679
            ]
          ]
        ]}}]}}

g = LMIPy.Geometry(attributes=atts)

[50]:
ds = LMIPy.Dataset(id_hash='a4e9c32d-d037-4c50-a893-967cad193537')
ds
[50]:
Population Density
GEE asset: CIESIN/GPWv4/population-density/2015 🗺Dataset in GFW.
Last Modified: 2019-03-08T18:24:06.420Z
Connector: gee | Published: True
[51]:
ds.layers[0].map(geometry=g)
[51]:

Finally, you can call the intersect function on the dataset object to see a dictionary of values.

[52]:
ds.intersect(geometry=g)
[52]:
{'population-density': {'count': 138767,
  'max': 5984.53564453125,
  'mean': 1.5942131871343077,
  'min': 0.0,
  'stdev': 31.059972394495446,
  'sum': 221224.18133906648}}

Layer Intersections

Intersections against layers work in the same manner. Here we show an intersect between an area along Brazil’s coast and a Mangrove biomass density Layer.

[53]:
atts={'geojson': {'type': 'FeatureCollection',
  'features': [{'type': 'Feature',
    'properties': {},
    'geometry': {'type': 'Polygon',
     'coordinates':

[
          [
            [
              -52.16308593749999,
              -1.669685500986571
            ],
            [
              -46.9775390625,
              -1.669685500986571
            ],
            [
              -46.9775390625,
              0.7909904981540058
            ],
            [
              -52.16308593749999,
              0.7909904981540058
            ],
            [
              -52.16308593749999,
              -1.669685500986571
            ]
          ]
        ]
                 }}]}}

g = LMIPy.Geometry(attributes=atts)

[54]:
ly = LMIPy.Layer('3ef039ba-29c7-459d-aadd-66d6e6cc8f5e')
ly
[54]:
Mangrove biomass density
GEE asset: None 🗺Layer in GFW.
Last Modified: 2019-06-11T20:36:06.441Z
Connector: gee | Published: True
[55]:
ly.map(geometry=g)
[55]:
[56]:
ly.intersect(geometry=g)
[56]:
{'b1': {'count': 135511,
  'max': 32.262001037597656,
  'mean': 2.3417258825859957,
  'min': 0.5376999974250793,
  'stdev': 5.0781633479771395,
  'sum': 302242.0047789521}}

ImageCollection: Search for satellite images

You can search for Landsat-8 and Sentinel-2 imagery using the ImageCollection module as follows:

[57]:
ic = LMIPy.ImageCollection(lon=28.3, lat=-16.6, start='2018-08-01', end='2018-08-10') # n.b. lon/lat are temporarily flipped
[58]:
ic
[58]:
Image Source: Sentinel-2A
Datetime: 2018-08-08 07:56:11Z
Cloud score : 0

Image Source: LANDSAT_8
Datetime: 2018-08-05 08:11:14Z
Cloud score : 0

Image Source: Sentinel-2B
Datetime: 2018-08-03 07:56:09Z
Cloud score : 0
[59]:
type(ic)
[59]:
LMIPy.imageCollection.ImageCollection

All types of pythonic list mainipulation are supported on the image collection results. Including iteration:

[60]:
for i in ic[0:2]:
    print(i)
Image COPERNICUS/S2/20180808T075611_20180808T081515_T35KPB
Image LANDSAT/LC08/C01/T1_RT_TOA/LC08_172071_20180805

…and subsetting to access individual Images.

Images: Individual satellite tiles

[61]:
i = ic[0]
i
[61]:
Image Source: Sentinel-2A
Datetime: 2018-08-08 07:56:11Z
Cloud score : 0
[62]:
type(i)
[62]:
LMIPy.image.Image

You can access the Image attributes, which indicate the provinence of the tile.

[63]:
i.attributes
[63]:
{'provider': 'COPERNICUS/S2/20180808T075611_20180808T081515_T35KPB'}

Mapping Satellite Images

You can also display web-map tiles and the bounding-box of the satellite image.

[64]:
i.map()
[64]:

Classification of individual Satellite Images

[65]:
classified = i.classify()
classified
[65]:
Image Source: Classified Image
Datetime: 2018-08-08 07:56:11Z
Cloud score : 0
[66]:
classified.map()
[66]:

Composite and classify satellite image collections

First grab a collection, specifying a point and time period.

[67]:
ic = LMIPy.ImageCollection(lon=28.271979, lat=-16.457814, start='2018-06-01', end='2018-06-20')
ic
[67]:
Image Source: Sentinel-2A
Datetime: 2018-06-19 07:56:11Z
Cloud score : 0

Image Source: LANDSAT_8
Datetime: 2018-06-18 08:10:49Z
Cloud score : 0.21

Image Source: Sentinel-2B
Datetime: 2018-06-14 07:56:09Z
Cloud score : 1.4529

Image Source: Sentinel-2B
Datetime: 2018-06-14 07:56:09Z
Cloud score : 14.467

Image Source: Sentinel-2A
Datetime: 2018-06-09 07:56:11Z
Cloud score : 0.011

Image Source: Sentinel-2B
Datetime: 2018-06-04 07:56:09Z
Cloud score : 0

Image Source: LANDSAT_8
Datetime: 2018-06-02 08:10:40Z
Cloud score : 0.06

Next ask for a composite image based on your collection. By default it will be for Sentinel-2, but you may change this via an argument to Landsat.

[68]:
i = ic.composite()
i
[68]:
Image Source: Composite Image
Datetime: 2018-06-01–2018-06-20
Cloud score : -

You can then map your composite image.

[69]:
i.map()
[69]:

You can take your composite image, and apply a land cover classifier based on our pre-trained models. These are Deepvel, and Segnet.

[72]:
c = i.classify(model_type='segnet', version='v2')
c
[72]:
Image Source: Classified Image
Datetime: 2018-06-01–2018-06-20
Cloud score : -
[73]:
c.map()
[73]:

Construct Widgets

A demo of how to use LMIPy to construct widgets like those on our projects such as Global Forest Watch Dashboard pages.

Tree cover example

[74]:
import matplotlib.pyplot as plt
%matplotlib inline
[75]:
# Get a datatable (Hansen)
table = LMIPy.Table('a20e9c0e-8d7d-422f-90f5-3b9bca355aaf')
table
[75]:
country page data for admin level 2
Data source json 🗺Table in GFW.
Last Modified: 2018-11-21T14:02:21.137Z
Connector: json | Published: True
[76]:
iso = 'BRA'
administration = 1

sql = f"""
        SELECT
            SUM(area_extent) as value,
            SUM(area_admin) as total_area
        FROM data
        WHERE iso = '{iso}'
        AND adm1 = {administration}
        AND thresh = 30
        AND polyname = 'admin'
"""

results = table.query(sql=sql)
results
[76]:
total_area value
0 1.527330e+07 1.140593e+07
[77]:
sizes = [results.value[0], results.total_area[0] - results.value[0]]
colors = ['green','grey']
labels = ['Tree cover', 'Non-forest']

fig1, ax1 = plt.subplots()
ax1.pie(sizes, labels=labels, autopct='%1.1f%%',
        shadow=False, startangle=90, colors=colors)
ax1.axis('equal')
centre_circle = plt.Circle((0,0),0.75,color='black', fc='white',linewidth=0.5)
fig1 = plt.gcf()
fig1.gca().add_artist(centre_circle)
plt.suptitle('Tree cover extent')
plt.title(f'in {iso}/{administration}')
plt.show()
_images/quickstart_137_0.png

Tree cover loss example

[78]:
sql = """
SELECT
    polyname, year_data.year as year,
    SUM(year_data.area_loss) as area

FROM data
WHERE polyname = 'admin'
AND thresh= 30
GROUP BY polyname, iso, nested(year_data.year)
"""
global_loss = table.query(sql=sql)
global_loss.head()
[78]:
area iso polyname year
0 2.746361e+06 BRA admin 2001
1 3.507049e+06 BRA admin 2002
2 3.248527e+06 BRA admin 2003
3 3.848771e+06 BRA admin 2004
4 3.486563e+06 BRA admin 2005
[79]:
iso='BRA'
loss_data = list(global_loss[global_loss['iso'] == f'{iso}']['area'])
years = list(global_loss[global_loss['iso'] == f'{iso}']['year'])

width = 0.66
fig, ax = plt.subplots()
rects1 = ax.bar(years, loss_data, width, color='#FE5A8D')

# add some text for labels, title and axes ticks
ax.set_ylabel('Loss extent (ha)')
ax.set_title(f'Loss by year in {iso}')
plt.show()
_images/quickstart_140_0.png

Creating a local backup of Data objects

Save a local backup of a collection to a specified path. This creates a folder containing a JSON for each dataset and it’s associated Layers, Metadata and Vocabularies.

[80]:
col = LMIPy.Collection(app=['gfw'], env='production')
[81]:
path = './LMI-BACKUP'
[82]:
col.save(path)
  0%|          | 0/330 [00:00<?, ?it/s]
Saving to path: ./LMI-BACKUP
100%|██████████| 330/330 [01:29<00:00,  3.67it/s]
Save complete!

Load Data objects from local backup

You can also load a previous version from local backup.

[84]:
import os
[85]:
files = os.listdir(path)[0:3]
files
[85]:
['a92ceeea-684a-4d39-89e1-4ca97673cb5a.json',
 'b70f070b-c9ae-4452-aa8e-2280a2604666.json',
 '89755b9f-df05-4e22-a9bc-05217c8eafc8.json']
[86]:
ds_id = files[0].split('.json')[0]
ds_id
[86]:
'a92ceeea-684a-4d39-89e1-4ca97673cb5a'
[87]:
dataset = LMIPy.Dataset(ds_id)
[88]:
dataset
[88]:
Example NLCD Dataset
Data source json 🗺Dataset in GFW.
Last Modified: 2019-08-06T16:07:07.094Z
Connector: json | Published: True
[89]:
backup = dataset.load(path)
[90]:
backup
[90]:
Example NLCD Dataset
Data source json 🗺Dataset in GFW.
Last Modified: 2019-08-06T16:07:07.094Z
Connector: json | Published: True
[ ]: