Skip to content

Instantly share code, notes, and snippets.

@AlexArcPy
Created December 4, 2016 11:07
Show Gist options
  • Save AlexArcPy/e880f764966f7376f0657e4baab7261f to your computer and use it in GitHub Desktop.
Save AlexArcPy/e880f764966f7376f0657e4baab7261f to your computer and use it in GitHub Desktop.
Network analysis with ArcGIS Server
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Have you ever needed to run network analysis operations using a ready-to-use network analysis service available published on ArcGIS Server?\n",
"\n",
"Well, if you have just a couple of points for which you want to generate a service area or solve a route, using Python package `requests` would suffice. Here is the minimal code required to get a service area polygon from a point with drive-time of 6 seconds."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"{u'messages': [],\n",
" u'saPolygons': {u'features': [{u'attributes': {u'FacilityID': 1,\n",
" u'FromBreak': 0,\n",
" u'Name': u'Location 1 : 0 - 0.1',\n",
" u'ObjectID': 1,\n",
" u'Shape_Area': 2.4898261941040918e-08,\n",
" u'Shape_Length': 0.0018572526480212762,\n",
" u'ToBreak': 0.1},\n",
" u'geometry': {u'rings': [[[-117.11594581599996, 32.75610160800005],\n",
" [-117.11572074899999, 32.755205154000066],\n",
" [-117.11594581599996, 32.755880356000034],\n",
" [-117.11594581599996, 32.75610160800005]]]}}],\n",
" u'fieldAliases': {u'FacilityID': u'FacilityID',\n",
" u'FromBreak': u'FromBreak',\n",
" u'Name': u'Name',\n",
" u'ObjectID': u'ObjectID',\n",
" u'Shape_Area': u'Shape_Area',\n",
" u'Shape_Length': u'Shape_Length',\n",
" u'ToBreak': u'ToBreak'},\n",
" u'geometryType': u'esriGeometryPolygon',\n",
" u'spatialReference': {u'latestWkid': 4326, u'wkid': 4326}}}"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import requests\n",
"import json\n",
"\n",
"service_url = r'http://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiego/NAServer/ServiceArea/solveServiceArea'\n",
"params = {'facilities':'-117.115844, 32.755628','f':'json','defaultBreaks':0.1}\n",
"\n",
"res = requests.get(service_url,params)\n",
"js = json.loads(res.content,encoding='UTF-8')\n",
"js"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you have your own GIS dataset stored within the Esri geodatabase, you might like using `arcpy` to read the point features for which you want to generate service areas. `arcpy` will help you to pull your features into a Python dict which you will be able to use when submitting a request to network server."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The first thing we need to do is to create an object in the format of `facilities` in the [ArcGIS REST API Solve Service Area](http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#/Solve_Service_Area/02r3000000pp000000/). It has to be a JSON like object; this one can be created using `da.SearchCursor` and a dictionary comprehension."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import arcpy\n",
"def dump_features_to_json(in_fc):\n",
" \"\"\"convert feature class into a JSON object\"\"\"\n",
" fields = ['StoreName','DriveTimeValue','SHAPE@X','SHAPE@Y']\n",
" records = [f for f in arcpy.da.SearchCursor(in_fc,fields)]\n",
" return {'features':[{'geometry':{\"x\":rec[2],\"y\":rec[3]},\n",
" 'attributes':{'Name':str(rec[0]),'Breaks_TravelTime':rec[1]}} \n",
" for rec in records]}"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false,
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"{'features': [{'attributes': {'Breaks_TravelTime': 2, 'Name': 'Store1'},\n",
" 'geometry': {'x': -117.16502024899995, 'y': 32.77923664100007}},\n",
" {'attributes': {'Breaks_TravelTime': 3, 'Name': 'Store2'},\n",
" 'geometry': {'x': -117.09124748399995, 'y': 32.77250827800003}},\n",
" {'attributes': {'Breaks_TravelTime': 4, 'Name': 'Store3'},\n",
" 'geometry': {'x': -117.03999642999997, 'y': 32.645928111000046}}]}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"in_fc = r'C:\\GIS\\Temp\\scratch.gdb\\Stores'\n",
"facilities = dump_features_to_json(in_fc)\n",
"facilities"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When we have the `facilities` object in the proper format, we are ready to submit the query to the network server:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false,
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"{u'features': [{u'attributes': {u'FacilityID': 3,\n",
" u'FromBreak': 0,\n",
" u'Name': u'Store3 : 0 - 4',\n",
" u'ObjectID': 1,\n",
" u'Shape_Area': 0.0011828843887082643,\n",
" u'Shape_Length': 0.21600680252984927,\n",
" u'ToBreak': 4},\n",
" u'geometry': {u'rings': [[[-117.02858543399998, 32.655267715000036],\n",
" [-117.02656364399996, 32.65459251400006],\n",
" [-117.02476692199997, 32.65504264800006],\n",
" [-117.02139854399996, 32.65324592600007],\n",
" [-117.02005004899996, 32.65302085900004],\n",
" [-117.01937675499994, 32.652349472000026],\n",
" [-117.01825332599998, 32.64965248100003],\n",
" [-117.01937675499994, 32.64673423800008],\n",
" [-117.02005004899996, 32.646059036000054],\n",
" [-117.02297019999997, 32.64516258200007],\n",
" [-117.02341842699997, 32.64359092700005],\n",
" [-117.02341842699997, 32.643365860000074],\n",
" [-117.02364349399994, 32.64291572600007],\n",
" [-117.02319526699995, 32.64291572600007],\n",
" [-117.02252006499998, 32.640893936000055],\n",
" [-117.02050018299997, 32.64179420500005],\n",
" [-117.01847839399994, 32.640668869000024],\n",
" [-117.01645660399998, 32.64111900300003],\n",
" [-117.01555824299999, 32.64044761700006],\n",
" [-117.01286315899995, 32.63977241500004],\n",
" [-117.01151657099996, 32.636404037000034],\n",
" [-117.01286315899995, 32.63326072700005],\n",
" [-117.01443672199997, 32.632810593000045],\n",
" [-117.01443672199997, 32.63258552600007],\n",
" [-117.01600837699999, 32.631910324000046],\n",
" [-117.01668167099996, 32.63213539100008],\n",
" [-117.01735496499998, 32.631238937000035],\n",
" [-117.02072524999994, 32.629892349000045],\n",
" [-117.02162170399998, 32.630338669000025],\n",
" [-117.02207183799999, 32.62876701400006],\n",
" [-117.02297019999997, 32.62876701400006],\n",
" [-117.02297019999997, 32.62831687900007],\n",
" [-117.02836036699995, 32.62584877000006],\n",
" [-117.02903366099997, 32.626298904000066],\n",
" [-117.03038024899996, 32.62584877000006],\n",
" [-117.03240203899998, 32.62697029100008],\n",
" [-117.03262710599995, 32.62697029100008],\n",
" [-117.03375053399998, 32.627645493000045],\n",
" [-117.034872055, 32.630338669000025],\n",
" [-117.03442382799994, 32.631464005000055],\n",
" [-117.03532218899994, 32.63326072700005],\n",
" [-117.03689384499995, 32.63370704700003],\n",
" [-117.03801727299998, 32.633035660000075],\n",
" [-117.03891563399998, 32.63370704700003],\n",
" [-117.03981399499997, 32.63348579400002],\n",
" [-117.04071235699996, 32.63393211400006],\n",
" [-117.03891563399998, 32.63011360200005],\n",
" [-117.03891563399998, 32.62854194600004],\n",
" [-117.03779220599995, 32.62652397200003],\n",
" [-117.03779220599995, 32.626298904000066],\n",
" [-117.03958892799994, 32.62293052700005],\n",
" [-117.04228401199998, 32.62180519100008],\n",
" [-117.045204163, 32.62293052700005],\n",
" [-117.04857253999995, 32.62944221500004],\n",
" [-117.05149078399995, 32.63213539100008],\n",
" [-117.05149078399995, 32.63236045800005],\n",
" [-117.05486106899997, 32.63460731500004],\n",
" [-117.05598258999999, 32.634157181000035],\n",
" [-117.05800437899995, 32.635282516000075],\n",
" [-117.06024932899999, 32.63483238200007],\n",
" [-117.06092452999997, 32.63258552600007],\n",
" [-117.06406784099994, 32.631238937000035],\n",
" [-117.06631278999998, 32.63236045800005],\n",
" [-117.06810951199998, 32.63236045800005],\n",
" [-117.06945800799997, 32.633035660000075],\n",
" [-117.06968307499994, 32.633035660000075],\n",
" [-117.07125472999996, 32.63370704700003],\n",
" [-117.07260131799995, 32.63685417200003],\n",
" [-117.07147979699994, 32.63977241500004],\n",
" [-117.07102966299999, 32.64044761700006],\n",
" [-117.06945800799997, 32.640893936000055],\n",
" [-117.06878471399995, 32.642465591000075],\n",
" [-117.06563949599996, 32.64381599400008],\n",
" [-117.06339454699997, 32.64269065900004],\n",
" [-117.06272125199996, 32.64291572600007],\n",
" [-117.06384277299998, 32.64404106100005],\n",
" [-117.064966202, 32.64426231400006],\n",
" [-117.064966202, 32.64448738100003],\n",
" [-117.06541442899999, 32.64448738100003],\n",
" [-117.06608962999996, 32.64561271700006],\n",
" [-117.06855964699997, 32.64650917100005],\n",
" [-117.06990623499996, 32.64965248100003],\n",
" [-117.06855964699997, 32.65347099300004],\n",
" [-117.06519126899997, 32.65481758100003],\n",
" [-117.05912780799997, 32.652349472000026],\n",
" [-117.05890273999995, 32.652349472000026],\n",
" [-117.05530929599996, 32.651002884000036],\n",
" [-117.05396270799997, 32.64785575900004],\n",
" [-117.05396270799997, 32.64673423800008],\n",
" [-117.05328750599995, 32.64673423800008],\n",
" [-117.05261421199998, 32.64875602700005],\n",
" [-117.05059242199997, 32.64942741400006],\n",
" [-117.05014419599996, 32.65010261500004],\n",
" [-117.04744911199998, 32.65122413600005],\n",
" [-117.04497909499997, 32.65032768200007],\n",
" [-117.04497909499997, 32.65077781700006],\n",
" [-117.04295730599995, 32.65144920300003],\n",
" [-117.04273223899997, 32.65144920300003],\n",
" [-117.04183387799998, 32.65212440500005],\n",
" [-117.04071235699996, 32.65189933800008],\n",
" [-117.03734397899996, 32.65055275000003],\n",
" [-117.03711891199998, 32.64965248100003],\n",
" [-117.03666877699999, 32.64965248100003],\n",
" [-117.03330039999997, 32.651002884000036],\n",
" [-117.03285217299998, 32.65189933800008],\n",
" [-117.03240203899998, 32.65189933800008],\n",
" [-117.03172874499995, 32.65392112700005],\n",
" [-117.02858543399998, 32.65504264800006],\n",
" [-117.02858543399998, 32.655267715000036]]]}},\n",
" {u'attributes': {u'FacilityID': 1,\n",
" u'FromBreak': 0,\n",
" u'Name': u'Store1 : 0 - 2',\n",
" u'ObjectID': 2,\n",
" u'Shape_Area': 0.00026086098928873504,\n",
" u'Shape_Length': 0.06301926000096955,\n",
" u'ToBreak': 2},\n",
" u'geometry': {u'rings': [[[-117.16175842299998, 32.77047538800008],\n",
" [-117.16535186799996, 32.76912879900004],\n",
" [-117.16849708599995, 32.77047538800008],\n",
" [-117.16894531299994, 32.77182579000004],\n",
" [-117.17096710199996, 32.77249717700005],\n",
" [-117.17164039599999, 32.77406883200007],\n",
" [-117.17231368999995, 32.77451896700006],\n",
" [-117.17298889199998, 32.776990891000025],\n",
" [-117.17433547999997, 32.77788734400008],\n",
" [-117.17433547999997, 32.77811241100005],\n",
" [-117.17545890799994, 32.78080558800008],\n",
" [-117.17276382399996, 32.787321091000024],\n",
" [-117.16827201799998, 32.789117813000075],\n",
" [-117.16759872399996, 32.79001426700006],\n",
" [-117.16490364099997, 32.79091453600006],\n",
" [-117.16490364099997, 32.791135788000076],\n",
" [-117.16063690199996, 32.789564133000056],\n",
" [-117.15928840599997, 32.786420822000025],\n",
" [-117.159513474, 32.78574943500007],\n",
" [-117.15839004499998, 32.78327751200004],\n",
" [-117.16041183499999, 32.77923393200007],\n",
" [-117.15861511199995, 32.77564048800008],\n",
" [-117.15928840599997, 32.77406883200007],\n",
" [-117.15928840599997, 32.77384376500004],\n",
" [-117.15996169999994, 32.772272110000074],\n",
" [-117.16108512899996, 32.77182579000004],\n",
" [-117.16175842299998, 32.77047538800008]]]}},\n",
" {u'attributes': {u'FacilityID': 2,\n",
" u'FromBreak': 0,\n",
" u'Name': u'Store2 : 0 - 3',\n",
" u'ObjectID': 3,\n",
" u'Shape_Area': 0.0002636764637841376,\n",
" u'Shape_Length': 0.06541538091404979,\n",
" u'ToBreak': 3},\n",
" u'geometry': {u'rings': [[[-117.09483528099997, 32.78282737700005],\n",
" [-117.09124183699998, 32.781255722000026],\n",
" [-117.09056854199997, 32.78148078900006],\n",
" [-117.08562660199999, 32.77923393200007],\n",
" [-117.08517837499994, 32.778562546000046],\n",
" [-117.08293151899994, 32.77788734400008],\n",
" [-117.08203315699996, 32.77564048800008],\n",
" [-117.07911491399994, 32.77474403400004],\n",
" [-117.07799148599997, 32.77160072300006],\n",
" [-117.07776641799995, 32.77160072300006],\n",
" [-117.07933998099998, 32.76800727800003],\n",
" [-117.08428001399994, 32.76598548900006],\n",
" [-117.08540344199997, 32.76598548900006],\n",
" [-117.08630180399996, 32.765310287000034],\n",
" [-117.09056854199997, 32.767107010000075],\n",
" [-117.09191513099995, 32.766881943000044],\n",
" [-117.09528350799997, 32.76867866500004],\n",
" [-117.09528350799997, 32.76890373200007],\n",
" [-117.09663200399996, 32.77137565600003],\n",
" [-117.09865188599997, 32.77204704300004],\n",
" [-117.09910201999998, 32.77317237900007],\n",
" [-117.10067367599999, 32.773618698000064],\n",
" [-117.10202217099999, 32.776990891000025],\n",
" [-117.10067367599999, 32.78035926800004],\n",
" [-117.09595870999999, 32.78260231000007],\n",
" [-117.09483528099997, 32.78260231000007],\n",
" [-117.09483528099997, 32.78282737700005]]]}}],\n",
" u'fieldAliases': {u'FacilityID': u'FacilityID',\n",
" u'FromBreak': u'FromBreak',\n",
" u'Name': u'Name',\n",
" u'ObjectID': u'ObjectID',\n",
" u'Shape_Area': u'Shape_Area',\n",
" u'Shape_Length': u'Shape_Length',\n",
" u'ToBreak': u'ToBreak'},\n",
" u'geometryType': u'esriGeometryPolygon',\n",
" u'spatialReference': {u'latestWkid': 4326, u'wkid': 4326}}"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"params = {'facilities':str(facilities),\n",
" 'f':'json',\n",
" 'defaultBreaks':20,\n",
" 'mergeSimilarPolygonRanges':'false',\n",
" 'outputLines':'esriNAOutputLineNone',\n",
" 'outputPolygons':'esriNAOutputPolygonSimplified',\n",
" 'overlapPolygons':'false',\n",
" 'trimOuterPolygon':'true',\n",
" 'trimPolygonDistance':500,\n",
" 'useHierarchy':'false',\n",
" 'impedanceAttributeName':'TravelTime' \n",
" }\n",
"\n",
"res = requests.get(service_url,params=params)\n",
"js = json.loads(res.content,encoding='UTF-8')\n",
"js = js['saPolygons']\n",
"js"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We have received back from server solved service areas, namely three polygons each of which represent the service area of specified drive time for each store. We would probably like to save it back into an Esri geodatabase."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"C:\\GIS\\Temp\\scratch.gdb\\StoresServiceAreas\n"
]
}
],
"source": [
"import os\n",
"arcpy.env.overwriteOutput = True\n",
"\n",
"#adding fields to the JSON\n",
"#to be able to create a feature class with JSONToFeatures GP tool\n",
"js['fields'] = [\n",
" {\n",
" \"name\" : \"Name\",\n",
" \"type\" : \"esriFieldTypeString\",\n",
" \"alias\" : \"Name\"\n",
" }]\n",
"\n",
"json_path = os.path.join(r'C:\\GIS\\Temp','out_stores_serviceareas.json')\n",
"with open(json_path,'wb') as tmp_json:\n",
" json.dump(js,tmp_json)\n",
"\n",
"out_fc = r'C:\\GIS\\Temp\\scratch.gdb\\StoresServiceAreas'\n",
"print arcpy.JSONToFeatures_conversion(json_path,out_fc)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(u'Store3 : 0 - 4', 17407426.312431492)\n",
"(u'Store1 : 0 - 2', 3844886.96011182)\n",
"(u'Store2 : 0 - 3', 3886107.862258967)\n"
]
}
],
"source": [
"for row in arcpy.da.SearchCursor(out_fc,['NAME','SHAPE@AREA'],\n",
" spatial_reference=arcpy.SpatialReference(3857)):\n",
" print row"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you are just learning about how the requests are submitted to the ArcGIS Server services, here is a great tip. Open the Services Directory and navigate to the REST end point you want to submit a request to. For the network server we have been using, it is `http://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiego/NAServer/ServiceArea/solveServiceArea`\n",
"\n",
"Then activate the Developer Tools window in your favourite web browser, submit a request with default parameters to begin with, and inspect the request that was submitted by watching over the `params` that have been sent. This is a great way to see what parameters are used and what values are being supplied."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Also, did you know that `ArcREST` Python package provides full support for network analysis operations when consuming an ArcGIS Server map service with Network Analysis capability enabled? If you have some other more advanced workflows and you are already using `ArcREST`, it is definitely worth checking out. Look for the network service source code [here](https://github.com/Esri/ArcREST/blob/master/src/arcrest/ags/_networkservice.py). I have added the support for NetworkServer in `ArcREST` several months ago and have used it myself on multiple occasions."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This is what is needed to do the same thing using `ArcREST`, now using a local ArcGIS Server service:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false,
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"{u'messages': [{u'description': u'Trim distance settings are not used because hierarchy is enabled.',\n",
" u'type': 50}],\n",
" u'saPolygons': {u'features': [{u'attributes': {u'FacilityID': 1,\n",
" u'FromBreak': 0,\n",
" u'Name': u'Location 1 : 0 - 1',\n",
" u'ObjectID': 1,\n",
" u'Shape_Area': 4.4966256129400014e-05,\n",
" u'Shape_Length': 0.03937942791395389,\n",
" u'ToBreak': 1},\n",
" u'geometry': {u'rings': [[[-117.11329104599997, 32.75672801300004],\n",
" [-117.11254505799997, 32.755729580000036],\n",
" [-117.11147999999997, 32.75620385600007],\n",
" [-117.11080089799998, 32.755220473000065],\n",
" [-117.10966594299998, 32.75573861300006],\n",
" [-117.10799597299996, 32.75510134800004],\n",
" [-117.10922483299998, 32.75406316100003],\n",
" [-117.11798458299995, 32.752572996000026],\n",
" [-117.11895034199995, 32.75329000000005],\n",
" [-117.11843272199997, 32.75353994200003],\n",
" [-117.11901999999998, 32.754444541000055],\n",
" [-117.12008289499994, 32.75385718700005],\n",
" [-117.12112080399999, 32.75512441500007],\n",
" [-117.12408862599995, 32.755290495000054],\n",
" [-117.11627329599997, 32.75840032800005],\n",
" [-117.11234833199995, 32.757131730000026],\n",
" [-117.11329104599997, 32.75672801300004]]]}}],\n",
" u'fieldAliases': {u'FacilityID': u'FacilityID',\n",
" u'FromBreak': u'FromBreak',\n",
" u'Name': u'Name',\n",
" u'ObjectID': u'ObjectID',\n",
" u'Shape_Area': u'Shape_Area',\n",
" u'Shape_Length': u'Shape_Length',\n",
" u'ToBreak': u'ToBreak'},\n",
" u'geometryType': u'esriGeometryPolygon',\n",
" u'spatialReference': {u'latestWkid': 4326, u'wkid': 4326}}}"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from arcrest.ags.server import Server\n",
"from arcrest import AGSTokenSecurityHandler\n",
"\n",
"#logging in to AGS with a user account\n",
"security_handler = AGSTokenSecurityHandler(username='RoutingUser',password='RoutingUser',\n",
" org_url='http://localhost:6080/arcgis')\n",
"rest_server = Server('http://localhost:6080/arcgis',security_handler)\n",
"\n",
"#switching the folder\n",
"rest_server.currentFolder = \"Transportation\"\n",
"\n",
"#getting network analysis layer for solving service areas\n",
"na_url = 'http://localhost:6080/arcgis/rest/services/Transportation/ServiceAreaService/NAServer'\n",
"na_service = [service for service in rest_server.services\n",
" if service.url == na_url][0]\n",
"na_service_servicearea_layer = na_service.serviceAreaLayers[0]\n",
"\n",
"#solving service areas submitting the GET request behind the scenes\n",
"facilities = '''-117.115844, 32.755628'''\n",
"result = na_service_servicearea_layer.solveServiceArea(facilities=facilities,\n",
" defaultBreaks=1,\n",
" returnFacilities=False)\n",
"result"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.10"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment