Import a set of parcels

Download the sample

This sample demonstrates how to import an already existing set of parcels to ArcGIS Urban. The geometry and optionally also attributes of parcels are loaded directly from a geoJSON file. Download the parcels in a geoJSON format used in this sample.

To test the code from this sample, set up your own Uppsala example urban model. This way you have an access to the relevant feature service and can create, update or delete objects. Once your urban model is set, remove the existing parcels. Make sure that all the parcels are deleted before importing the new parcels from an external file.

Import relevant libraries and the Urban API schema stored as a Python module. See the Get Started section to get instructions on how to export the schema to a Python module. The geopandas library is used to read parcels geometry from a geoJSON.

      
1
2
3
4
5
6
from sgqlc.operation import Operation
from sgqlc.endpoint.http import HTTPEndpoint
from urban_api_schema import urban_api_schema as schema

import geopandas as gpd
import re

Provide the endpoint_url and token variables. Replace the token variable with your token to authenticate the account which hosts the created model. You can read more about how to retrieve a token in the Urban API Overview section. Set a path to the parcels dataset.

     
1
2
3
4
5
token="ACCESS_TOKEN"
endpoint_url = 'https://urban-api.arcgis.com/graphql?token='+token
endpoint = HTTPEndpoint(endpoint_url)

dataset_geojson = 'LOCAL_PATH/parcels.geojson'

Use the geopandas to read the parcels to a parcels variables.

 
1
parcels = gpd.read_file(dataset_geojson)

To see the type and amount of data contained in the dataset, print the number of parcels, the names of attributes available in the dataset, and its spatial reference.

      
1
2
3
4
5
6
print("Number of parcels: {}, number of columns: {}".format(
    parcels.shape[0], parcels.shape[1])
    )
print("List of shapefile attributes headers: {}".format(list(parcels.columns)))
wkid = int(re.search(r'\d+', str(parcels.crs)).group())
print("Spatial reference: {}".format(wkid))

The output should look something like this:

   
1
2
3
Number of parcels: 45628, number of columns: 7
List of shapefile attributes headers: ['CustomID', 'Households', 'Jobs', 'Population', 'EdgeInfos', 'Suitabilit', 'geometry']
Spatial reference: 3857

As you can see, there are 45628 parcels available for the Uppsala example city. You will add the parcels in batches of 1000 each. Note, that this requires sending 46 mutations to write all the parcels to the urban model.

Define a helper function that gets a batch of parcels of a given size and saves it in the list, which can be later used in the GraphQL mutation. Each single parcel stores a geometry in a form of 2d rings, as well as the optional attributes: suitability score, custom ID, and existing values of population, jobs, and households.

                                            
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def get_batch(parcels, start, batch_size):

    end = start+batch_size

    # get a batch of parcels
    if end>len(parcels):
        parcels = parcels[start:len(parcels)]
    else:
        parcels = parcels[start:start+batch_size]

    # transform the geodataframe to a list of dictionaries
    # each row (that is parcel) is shown as a separate dictionary
    parcels_transformed = parcels.to_dict('index')

    parcels_list = []

    for _, attributes_dict in parcels_transformed.items():

        # initialize a single parcel object
        single_parcel = {
            'attributes': {},
            'geometry':{'rings':[], 'spatial_reference': {'wkid':wkid}}
        }

        # set the attributes for the single parcel
        attributes = {
            'custom_id': attributes_dict['CustomID'],
            'households': attributes_dict['Households'],
            'jobs': attributes_dict['Jobs'],
            'population': attributes_dict['Population'],
            'suitability_score': attributes_dict['Suitabilit']
        }

        # set a geometry of a single parcel (2d rings)
        geometry = [list(attributes_dict['geometry'].exterior.coords)]

        # save single parcel's attributes and geometry to the single parcel object
        single_parcel['attributes'] = attributes
        single_parcel['geometry']['rings'] = geometry

        # add a single parcel to the list
        parcels_list.append(single_parcel)

    return parcels_list

Make calls to the Urban API endpoint and add parcels to the model. Remember, you need to replace the urban_database_id variable with the urban database id value of the urban model you created. It might take a while until all the mutations are successfully sent to the endpoint.

                           
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
start = 0
batch_size = 1000

while True:

    parcels_list = get_batch(parcels, start, batch_size)

    # initialize the mutation
    op = Operation(schema.Mutation)

    # add parcels to the Uppsala urban model
    create_parcels = op.create_parcels(urban_database_id="885e8566549d44db93df3184521XXXXX",
                                      parcels=parcels_list)

    # select relevant return fields
    create_parcels.attributes.__fields__('global_id')

    # make a call to the endpoint
    json_data = endpoint(op)
    errors = json_data.get('errors')
    if errors:
        print(errors)

    start+=batch_size

    if start >= len(parcels):
        break

Go to ArcGIS Online and check if the parcels were added to your urban model.

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.