-
Description: Implementation of a programmatic API with a clean interface that consumes building limits and height plateaus split up the building limits according to the height plateaus, and stores these three entities (building limits, height plateaus and split building limits) in a persistent way.
-
Python choice: We have used Python as a programming language because it contains libraries for calculation and backend APIs (Flask framework) and it is now one of the most commonly used programming languages in the world.
-
Runtime environment:
mkdir geo_app # Create project directory. python3 -m venv ./geo_app_env # Create Python3 env-variable. source ./geo_app_env/bin/activate # Activat env-variable. cd geo_app # Go to Project folder code . # Open project directory using vscode
-
Create
requirements.txt
file that contains all dependencies librariespip3 install pipreqs pip3 install pip-tools pip3 freeze > requirements.txt
-
install project dependencies:
pip3 install -r requirements.txt
-
-
-
Connection - Google Sheet API
The first part of the code in the document establishes a connection to the google sheets API and opens the worksheet as wks.
scope = ['https://spreadsheets.google.com/feeds','https://www.googleapis.com/auth/drive'] sa = gspread.service_account('credentials.json', scope) sh = sa.open("output_final") wks = sh.worksheet("Sheet1")
-
Data configuration -
CreateColName
methodThe method updates the specified values in google sheets and inserts the names of the columns.
def createColNames(): wks.update('A1','ObjectID') wks.update('B1','ObjectType') wks.update('C1','Data') wks.update('D1','Plateaus')
-
API - endpoint handling
The class
api_requests
, contains handles the API endpoint for the application. When the user calls the API with a get request, all data from the google worksheet is returned in a JSON format When a post request is called, the input of the post body is stored and then forwarded to thenewInput
method.class api_requests(Resource): # Get all data splited def get(self): data = wks.get_all_values() return {'All Objects': data}, 200 # POST - data (BuildingLimits and Heigth plateaus) and return splited data. post request to upload new data to specific row in spreadsheet. def post(self): parser = reqparse.RequestParser() arguments = request.data reply = newInput(json.loads(arguments)) return reply, # API - /data endpoints to expose data api.add_resource(api_requests, '/data') ```
-
API - endpoint error handling
The
newInput(data)
function first calls thecheckInput
function whose task it is to check if the input is valid usingcheckInput(data)
method that will be described in the next section.def newInput(data): result = checkInput(data) # Error Handling - APIs spliting if result == 1: return 'Key(s) Missing! Building limit or height Plateau key is missing' elif result == 2: return 'The input Type is required to be a Feature Collection' elif result == 3: return 'The quantity of items in building limits is not equal to the one of height plateau' elif result == 4: return 'The total area of the building limits is not equal to the total area of the height plateaus' elif result ==5: return 'There are gaps between building limits or height plateaus outisde of building limits' elif result == 0: createLimits(data) createPlateaus(data) return 'Data succesfully uploaded' return result
-
API Input Data validation check
Now we will get to the different parts of the
checkInput
function. This first for loop checks if the keys ‘building limits’ as well as ‘height plateaus’ exist in the data that has been input.
# Verifies provided Data - makes sure that building limits and height plateaus as well as the indidvidual object type is 'polygon'
def checkInput(data):
build_temp = []
height_temp = []
build_area = 0
height_area = 0
valid = 0
# This first for loop checks if the keys ‘building limits’ as well as ‘height plateaus’ exist in the data that has been input.
for item in data.keys():
if item != 'building_limits' and item != 'height_plateaus':
# Checks if 'building limits' and 'height_plateaus' exist
valid = 1
break
else:
valid = 0
if valid == 0:
if data['building_limits']['type'] == 'FeatureCollection' and data['height_plateaus']['type'] == 'FeatureCollection':
# Checks if both inputs are feature collections
pass
else:
valid = 2
buildLen = len(data['building_limits']['features'])
heightLen = len(data['height_plateaus']['features'])
if buildLen != heightLen:
# Checks if height_plateaus and building_limits have the same amount of data points
valid = 3
else:
# Calculates total area of height plateaus and building limits and checks if they are equal.
for items in data['building_limits']['features']:
for items2 in items['geometry']['coordinates']:
for items3 in items2:
build_area = build_area + (items3[0] + items3[1])
build_temp.append(items3[0])
build_temp.append(items3[1])
for items in data['height_plateaus']['features']:
for items2 in items['geometry']['coordinates']:
for items3 in items2:
height_area = height_area + (items3[0] + items3[1])
height_temp.append(items3[0])
height_temp.append(items3[1])
if height_area != build_area:
# Checks of building limit area matches height plateau area.
valid = 4
elif height_area == build_area:
# Checks for gaps or building limit exceedings.
if not np.array_equal(build_temp,height_temp):
valid = 5
return valid
The next part of the code checks if the type of the input is a collection of features for both, building limits and height plateaus.
# This part of the code checks if the type of the input is a collection of features for both, building limits and height plateaus
if valid == 0:
if data['building_limits']['type'] == 'FeatureCollection' and data['height_plateaus']['type'] == 'FeatureCollection':
# Checks if both inputs are feature collections
pass
else:
valid = 2
The next part retrieves all individual coordinates and stores them in a temporary array. In the end, the sum is calculated and compared between height plateaus and building limit.
# The next part retrieves all individual coordinates and stores them in a temporary array. At the end the sum is calculated and compared between height plateaus and building limit.
# (calculates total area of height plateaus and building limits and checks if they are equal)
for items in data['building_limits']['features']:
for items2 in items['geometry']['coordinates']:
for items3 in items2:
build_area = build_area + (items3[0] + items3[1])
build_temp.append(items3[0])
build_temp.append(items3[1])
for items in data['height_plateaus']['features']:
for items2 in items['geometry']['coordinates']:
for items3 in items2:
height_area = height_area + (items3[0] + items3[1])
height_temp.append(items3[0])
height_temp.append(items3[1])
if height_area != build_area:
# Checks of building limit area matches height plateau area
valid = 4
The following method checks for gaps by comparing the values within the 2 coordinate arrays.
# This following method checks for gaps by comparing the values within the 2 coordinate arrays.
elif height_area == build_area:
# Checks for gaps or building limit exceedings
if not np.array_equal(build_temp,height_temp):
valid = 5
The result of valid
is then returned to ‚newInput‘ and interpreted accordingly to the value it returned. Based on this the JSON data is then either uploaded or an error is returned to the user
# Error Handling - APIs spliting
if result == 1:
return 'Key(s) Missing! Building limit or height Plateau key is missing'
elif result == 2:
return 'The input Type is required to be a Feature Collection'
elif result == 3:
return 'The quantity of items in building limits is not equal to the one of height plateau'
elif result == 4:
return 'The total area of the building limits is not equal to the total area of the height plateaus'
elif result ==5:
return 'There are gaps between building limits or height plateaus outisde of building limits'
elif result == 0:
createLimits(data)
createPlateaus(data)
return 'Data succesfully uploaded'
return result
We then have two methods that extract all building limits as well as all height plateaus. At the end of this function, a new object is created.
# Extracts data from json and creates NewPlateauObject if exist on Google Sheet
def createPlateaus(data):
plateauToImport =data['height_plateaus']['features']
lenPlateauToImport =len(data['height_plateaus']['features'])
for items in range(lenPlateauToImport):
id = increaseID()
plateaus = getPlateaus(data)
plateaus = plateaus[items]
plateauData = plateauToImport[items]['geometry']['coordinates']
NewPlateauObject(str(plateaus),str(plateauData),str(id))
# Extracts data from json and creates NewLimitObject on Google Sheet
def createLimits(data):
limitsToImport =data['building_limits']['features']
lenBuildingToImport =len(data['building_limits']['features'])
for items in range(lenBuildingToImport):
id = increaseID()
limitData = limitsToImport[items]['geometry']['coordinates']
NewLimitObject(str(limitData),str(id))
With the object creation, the data is written to the google sheet
# Creates new limit line on google sheet
class NewLimitObject:
def __init__(self,data,id):
self.id = id
self.data = data
# Uploads values to google sheet
wks.update('A'+ id, id)
wks.update('B'+ id, 'Building Limit')
wks.update('C'+ id, data)
# Creates new plateau line on google sheet
class NewPlateauObject:
def __init__(self,plateau,plateauData,id):
self.id = id
self.data = data
#uploads values to google sheet
wks.update('A'+ id, id)
wks.update('B'+ id, 'Plateau')
wks.update('C'+ id, plateauData)
wks.update('D'+ id, plateau)
The last function which is specific to the height plateaus extracts the elevation
element from the height plateau ‘elevation’ property on the input JSON data file.
def getPlateaus(data):
elevations = []
for i in range(len(data['height_plateaus']['features'])):
x = data['height_plateaus']['features'][i]['properties']['elevation']
elevations.append(x)
return elevations
-
API Deployment GCP Cloud
Creates a Dockerfile with the name Dockerfile and the following lines of code to it.
FROM python:3 WORKDIR /usr/src/app COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD [ "python", "./app.py" ]
Now your project is ready to be deployed. To deploy it to Google Cloud Run, we need to containerize the project and the container to Google Container Registry (gcr).
Let’s see how it can be done.
-
Go to your project directory and open up the terminal.
-
Type gcloud init and select the GCP project in which you want to deploy and note the project id.
-
Type export project_id=THE_PROJECT_ID_NOTED (project-360821).
-
Type gcloud builds submit –tag gcr.io/${project_id}/geo_app:v1. (This will take a few minutes to complete.)
-
Type gcloud run deploy –image gcr.io/${project_id}/geo_app:v1 –platform managed
-
You will be prompted for the service name: press ENTER to accept the default name, geo_app.
-
You will be prompted for region: select the region of your choice.
-
You will be prompted to allow unauthenticated invocations: respond y
-
Then wait for a few moments until the deployment is complete. On success, the command line displays the service URL
https://said-project-360821.lm.r.appspot.com/
. -
API Test - Postman
Visit your deployed container by opening the service URL=https://said-project-360821.lm.r.appspot.com/data
in a web browser by adding the path /data
you will get data from Google Sheet via the API deployed:
- Get All Data splited - via API deployed
- POST Data and saved on Gsheet- via API deployed - tested via postman
- Save all data splited
-
Postman API testing Documentation via postman if you want to develop an application that will consume our API service: API testing documentation and also different language code (js, py, java..). You can test the API via the URL below:
- Data input - Assumption - based on
https://geojson.io
In order to create our JSON data we have used the https://geojson.io/#map=8/62.063/15.238
For testing our API we made data input assumption we have tested with 4 types of inputs (JSON valid data json file
, Keys missing.json
, Ǹot Feature collection json data
and Height plateaus outside of building limit json file data
, then total sizes are not matching
).
Below you have all type of JSON iput data:
- Case 1:
ValidData.json
-> BuildingLimits, HeightPlateaus -> valid inputs.
{
"building_limits":
{ "type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.41552734375,
49.710272582105695
],
[
10.96435546875,
49.710272582105695
],
[
10.96435546875,
51.33061163769853
],
[
8.41552734375,
51.33061163769853
],
[
8.41552734375,
49.710272582105695
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.404296875,
50.00773901463687
],
[
10.590820312499998,
50.00773901463687
],
[
10.590820312499998,
50.56928286558243
],
[
9.404296875,
50.56928286558243
],
[
9.404296875,
50.00773901463687
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.72314453125,
50.764259357116465
],
[
9.4921875,
50.764259357116465
],
[
9.4921875,
51.20688339486559
],
[
8.72314453125,
51.20688339486559
],
[
8.72314453125,
50.764259357116465
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.8876953125,
50.736455137010665
],
[
10.6787109375,
50.736455137010665
],
[
10.6787109375,
51.193115244645874
],
[
9.8876953125,
51.193115244645874
],
[
9.8876953125,
50.736455137010665
]
]
]
}
}
]
},"height_plateaus":
{ "type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {"elevation":4},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.41552734375,
49.710272582105695
],
[
10.96435546875,
49.710272582105695
],
[
10.96435546875,
51.33061163769853
],
[
8.41552734375,
51.33061163769853
],
[
8.41552734375,
49.710272582105695
]
]
]
}
},
{
"type": "Feature",
"properties": {"elevation":4},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.404296875,
50.00773901463687
],
[
10.590820312499998,
50.00773901463687
],
[
10.590820312499998,
50.56928286558243
],
[
9.404296875,
50.56928286558243
],
[
9.404296875,
50.00773901463687
]
]
]
}
},
{
"type": "Feature",
"properties": {"elevation":6},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.72314453125,
50.764259357116465
],
[
9.4921875,
50.764259357116465
],
[
9.4921875,
51.20688339486559
],
[
8.72314453125,
51.20688339486559
],
[
8.72314453125,
50.764259357116465
]
]
]
}
},
{
"type": "Feature",
"properties": {"elevation":5},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.8876953125,
50.736455137010665
],
[
10.6787109375,
50.736455137010665
],
[
10.6787109375,
51.193115244645874
],
[
9.8876953125,
51.193115244645874
],
[
9.8876953125,
50.736455137010665
]
]
]
}
}
]
}
}
- Case 2:
Keys missing.json
->building_limitss
,height_plateaaus
keys aren't valid. There is a typo in "building limits causing json interpreter to not recognize the keys"
{
"building_limitss":
{ "type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.41552734375,
49.710272582105695
],
[
10.96435546875,
49.710272582105695
],
[
10.96435546875,
51.33061163769853
],
[
9.41552734375,
50.33061163769853
],
[
8.41552734375,
49.710272582105695
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.404296875,
50.00773901463687
],
[
10.590820312499998,
50.00773901463687
],
[
10.590820312499998,
50.56928286558243
],
[
9.404296875,
50.56928286558243
],
[
9.404296875,
50.00773901463687
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.72314453125,
50.764259357116465
],
[
9.4921875,
50.764259357116465
],
[
9.4921875,
51.20688339486559
],
[
8.72314453125,
51.20688339486559
],
[
8.72314453125,
50.764259357116465
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.8876953125,
50.736455137010665
],
[
10.6787109375,
50.736455137010665
],
[
10.6787109375,
51.193115244645874
],
[
9.8876953125,
51.193115244645874
],
[
9.8876953125,
50.736455137010665
]
]
]
}
}
]
},"height_plateaaus":
{ "type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {"elevation":4},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.41552734375,
49.710272582105695
],
[
10.96435546875,
49.710272582105695
],
[
10.96435546875,
51.33061163769853
],
[
8.41552734375,
51.33061163769853
],
[
8.41552734375,
49.710272582105695
]
]
]
}
},
{
"type": "Feature",
"properties": {"elevation":4},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.404296875,
50.00773901463687
],
[
10.590820312499998,
50.00773901463687
],
[
10.590820312499998,
50.56928286558243
],
[
9.404296875,
50.56928286558243
],
[
9.404296875,
50.00773901463687
]
]
]
}
},
{
"type": "Feature",
"properties": {"elevation":6},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.72314453125,
50.764259357116465
],
[
9.4921875,
50.764259357116465
],
[
9.4921875,
51.20688339486559
],
[
8.72314453125,
51.20688339486559
],
[
8.72314453125,
50.764259357116465
]
]
]
}
},
{
"type": "Feature",
"properties": {"elevation":5},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.8876953125,
50.736455137010665
],
[
10.6787109375,
50.736455137010665
],
[
10.6787109375,
51.193115244645874
],
[
9.8876953125,
51.193115244645874
],
[
9.8876953125,
50.736455137010665
]
]
]
}
}
]
}
}
- Case 3:
noFeatureCollection.json
-> sadsFeatureCollection -> isn't FeatureCollection ("The input Type is required to be a Feature Collection").
{
"building_limits":
{ "type": "sadsFeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.41552734375,
49.710272582105695
],
[
10.96435546875,
49.710272582105695
],
[
10.96435546875,
51.33061163769853
],
[
8.41552734375,
51.33061163769853
],
[
8.41552734375,
49.710272582105695
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.404296875,
50.00773901463687
],
[
10.590820312499998,
50.00773901463687
],
[
10.590820312499998,
50.56928286558243
],
[
9.404296875,
50.56928286558243
],
[
9.404296875,
50.00773901463687
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.72314453125,
50.764259357116465
],
[
9.4921875,
50.764259357116465
],
[
9.4921875,
51.20688339486559
],
[
8.72314453125,
51.20688339486559
],
[
8.72314453125,
50.764259357116465
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.8876953125,
50.736455137010665
],
[
10.6787109375,
50.736455137010665
],
[
10.6787109375,
51.193115244645874
],
[
9.8876953125,
51.193115244645874
],
[
9.8876953125,
50.736455137010665
]
]
]
}
}
]
},"height_plateaus":
{ "type": "Polygon",
"features": [
{
"type": "Feature",
"properties": {"elevation":4},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.41552734375,
49.710272582105695
],
[
10.96435546875,
49.710272582105695
],
[
10.96435546875,
51.33061163769853
],
[
8.41552734375,
51.33061163769853
],
[
8.41552734375,
49.710272582105695
]
]
]
}
},
{
"type": "Feature",
"properties": {"elevation":4},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.404296875,
50.00773901463687
],
[
10.590820312499998,
50.00773901463687
],
[
10.590820312499998,
50.56928286558243
],
[
9.404296875,
50.56928286558243
],
[
9.404296875,
50.00773901463687
]
]
]
}
},
{
"type": "Feature",
"properties": {"elevation":6},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.72314453125,
50.764259357116465
],
[
9.4921875,
50.764259357116465
],
[
9.4921875,
51.20688339486559
],
[
8.72314453125,
51.20688339486559
],
[
8.72314453125,
50.764259357116465
]
]
]
}
},
{
"type": "Feature",
"properties": {"elevation":5},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.8876953125,
50.736455137010665
],
[
10.6787109375,
50.736455137010665
],
[
10.6787109375,
51.193115244645874
],
[
9.8876953125,
51.193115244645874
],
[
9.8876953125,
50.736455137010665
]
]
]
}
}
]
}
}
- Case 4:
HpOutsideOfBL.json
-> Height plateaus outside of building limit: Building limit is exceeded by the height plateaus = by manual not allowed
{
"building_limits":
{ "type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.41552734375,
49.710272582105695
],
[
10.96435546875,
49.710272582105695
],
[
10.96435546875,
51.33061163769853
],
[
9.41552734375,
50.33061163769853
],
[
8.41552734375,
49.710272582105695
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.404296875,
50.00773901463687
],
[
10.590820312499998,
50.00773901463687
],
[
10.590820312499998,
50.56928286558243
],
[
9.404296875,
50.56928286558243
],
[
9.404296875,
50.00773901463687
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.72314453125,
50.764259357116465
],
[
9.4921875,
50.764259357116465
],
[
9.4921875,
51.20688339486559
],
[
8.72314453125,
51.20688339486559
],
[
8.72314453125,
50.764259357116465
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.8876953125,
50.736455137010665
],
[
10.6787109375,
50.736455137010665
],
[
10.6787109375,
51.193115244645874
],
[
9.8876953125,
51.193115244645874
],
[
9.8876953125,
50.736455137010665
]
]
]
}
}
]
},"height_plateaus":
{ "type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {"elevation":4},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.41552734375,
49.710272582105695
],
[
10.96435546875,
49.710272582105695
],
[
10.96435546875,
51.33061163769853
],
[
8.41552734375,
51.33061163769853
],
[
8.41552734375,
49.710272582105695
]
]
]
}
},
{
"type": "Feature",
"properties": {"elevation":4},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.404296875,
50.00773901463687
],
[
10.590820312499998,
50.00773901463687
],
[
10.590820312499998,
50.56928286558243
],
[
9.404296875,
50.56928286558243
],
[
9.404296875,
50.00773901463687
]
]
]
}
},
{
"type": "Feature",
"properties": {"elevation":6},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.72314453125,
50.764259357116465
],
[
9.4921875,
50.764259357116465
],
[
9.4921875,
51.20688339486559
],
[
8.72314453125,
51.20688339486559
],
[
8.72314453125,
50.764259357116465
]
]
]
}
},
{
"type": "Feature",
"properties": {"elevation":5},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.8876953125,
50.736455137010665
],
[
10.6787109375,
50.736455137010665
],
[
10.6787109375,
51.193115244645874
],
[
9.8876953125,
51.193115244645874
],
[
9.8876953125,
50.736455137010665
]
]
]
}
}
]
}
}
- Case 4:
total sizes are not matching.json
-> by manual we are not allowed to have gaps (building limit but no height plateau) if total area of building limit is not equal to total area of heigh plateau we get error /check input
{
"building_limits":
{ "type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.41552734375,
49.710272582105695
],
[
10.96435546875,
49.710272582105695
],
[
10.96435546875,
51.33061163769853
],
[
8.41552734375,
51.33061163769853
],
[
8.41552734375,
49.710272582105695
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.404296875,
50.00773901463687
],
[
9.404296875,
50.00773901463687
],
[
10.590820312499998,
50.00773901463687
],
[
10.590820312499998,
50.56928286558243
],
[
9.404296875,
50.56928286558243
],
[
9.404296875,
50.00773901463687
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.72314453125,
50.764259357116465
],
[
9.4921875,
50.764259357116465
],
[
9.4921875,
51.20688339486559
],
[
8.72314453125,
51.20688339486559
],
[
8.72314453125,
50.764259357116465
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.8876953125,
50.736455137010665
],
[
10.6787109375,
50.736455137010665
],
[
10.6787109375,
51.193115244645874
],
[
9.8876953125,
51.193115244645874
],
[
9.8876953125,
50.736455137010665
]
]
]
}
}
]
},"height_plateaus":
{ "type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {"elevation":4},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.41552734375,
49.710272582105695
],
[
10.96435546875,
49.710272582105695
],
[
10.96435546875,
51.33061163769853
],
[
8.41552734375,
51.33061163769853
],
[
8.41552734375,
49.710272582105695
]
]
]
}
},
{
"type": "Feature",
"properties": {"elevation":4},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.404296875,
50.00773901463687
],
[
10.590820312499998,
50.00773901463687
],
[
10.590820312499998,
50.56928286558243
],
[
9.404296875,
50.56928286558243
],
[
9.404296875,
50.00773901463687
]
]
]
}
},
{
"type": "Feature",
"properties": {"elevation":6},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.72314453125,
50.764259357116465
],
[
9.4921875,
50.764259357116465
],
[
9.4921875,
51.20688339486559
],
[
8.72314453125,
51.20688339486559
],
[
8.72314453125,
50.764259357116465
]
]
]
}
},
{
"type": "Feature",
"properties": {"elevation":5},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.8876953125,
50.736455137010665
],
[
10.6787109375,
50.736455137010665
],
[
10.6787109375,
51.193115244645874
],
[
9.8876953125,
51.193115244645874
],
[
9.8876953125,
50.736455137010665
]
]
]
}
}
]
}
}