Our organization hosts several geocode services from both regular and composite address locators. As we all should know, it is important to rebuild an address locator as changes are made to the reference data. Since the locator gets copied to the server, rebuilding the original locator is not sufficient so I've found the entire service needs to also be "rebuilt" (overwritten). We will create scheduled tasks that run a Python script to automate this process.
Having experienced the nightmare that occurs when an address locator gets "corrupted" and cannot be rebuilt, I have made the choice to simply recreate the locator each time instead of rebuilding it. The advantage of this method is that you aren't dead in the water should something tragic happen to the original locator because the specifications for creating it are in the script. Alternatively, you could just ensure the specifications of the locator are well documented in the script and just do a rebuild. Either way, the geocode service still needs to get republished with the new address locator reference data.
Here is a simplified process I use to rebuild a geocode service.
- Create temporary directory where the address locator and service definition files will be staged. This is cleaned up automatically when the script ends.
- Create address locator
- Create and analyze service definition draft file for sharing the address locator
- Modify Service Definition Draft file to overwrite the existing service.
- Convert Service Definition Draft to a Service Definition file
- Upload and publish service definition file as a service
To clarify, this was all tested on 10.2.2.
<SPAN class="keyword token">import</SPAN> arcpy
<SPAN class="keyword token">from</SPAN> contextlib <SPAN class="keyword token">import</SPAN> contextmanager
<SPAN class="keyword token">import</SPAN> datetime
<SPAN class="keyword token">import</SPAN> os
<SPAN class="keyword token">import</SPAN> shutil
<SPAN class="keyword token">import</SPAN> tempfile
<SPAN class="keyword token">import</SPAN> xml<SPAN class="punctuation token">.</SPAN>dom<SPAN class="punctuation token">.</SPAN>minidom <SPAN class="keyword token">as</SPAN> DOM <SPAN class="comment token">## sddTypeReplacement()</SPAN>
<SPAN class="keyword token">def</SPAN> <SPAN class="token function">main</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">:</SPAN>
<SPAN class="comment token"># Local variables</SPAN>
agsconn <SPAN class="operator token">=</SPAN> r<SPAN class="string token">"C:\test\MyAdminArcGISServerConnection.ags"</SPAN>
sdeconn <SPAN class="operator token">=</SPAN> r<SPAN class="string token">"C:\test\MySDEConnection.sde"</SPAN>
my_fancy_source_data <SPAN class="operator token">=</SPAN> os<SPAN class="punctuation token">.</SPAN>path<SPAN class="punctuation token">.</SPAN>join<SPAN class="punctuation token">(</SPAN>sdeconn<SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"my_fancy_source_data"</SPAN><SPAN class="punctuation token">)</SPAN>
locator_name <SPAN class="operator token">=</SPAN> <SPAN class="string token">"MY_FANCY_LOCATOR"</SPAN>
<SPAN class="keyword token">with</SPAN> makeTempDir<SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="keyword token">as</SPAN> temp_dir<SPAN class="punctuation token">:</SPAN>
<SPAN class="comment token"># Create address locator</SPAN>
temp_locator <SPAN class="operator token">=</SPAN> os<SPAN class="punctuation token">.</SPAN>path<SPAN class="punctuation token">.</SPAN>join<SPAN class="punctuation token">(</SPAN>temp_dir<SPAN class="punctuation token">,</SPAN> locator_name<SPAN class="punctuation token">)</SPAN>
field_info<SPAN class="operator token">=</SPAN><SPAN class="punctuation token">(</SPAN>
<SPAN class="string token">"'Feature ID' OBJECTID VISIBLE NONE;"</SPAN>
<SPAN class="string token">"'*House Number' ADDR_NUM VISIBLE NONE;"</SPAN>
<SPAN class="string token">"Side <None> VISIBLE NONE;"</SPAN>
<SPAN class="string token">"'Prefix Direction' PRE_DIR VISIBLE NONE;"</SPAN>
<SPAN class="string token">"'Prefix Type' <None> VISIBLE NONE;"</SPAN>
<SPAN class="string token">"'*Street Name' NAME VISIBLE NONE;"</SPAN>
<SPAN class="string token">"'Suffix Type' STREET_TYPE VISIBLE NONE;"</SPAN>
<SPAN class="string token">"'Suffix Direction' SUF_DIR VISIBLE NONE;"</SPAN>
<SPAN class="string token">"'City or Place' <None> VISIBLE NONE;"</SPAN>
<SPAN class="string token">"'ZIP Code' <None> VISIBLE NONE;"</SPAN>
<SPAN class="string token">"State <None> VISIBLE NONE;"</SPAN>
<SPAN class="string token">"Longitude <None> VISIBLE NONE;"</SPAN>
<SPAN class="string token">"Latitude <None> VISIBLE NONE;"</SPAN>
<SPAN class="string token">"'Street ID' <None> VISIBLE NONE;"</SPAN>
<SPAN class="string token">"'Min X value for extent' <None> VISIBLE NONE;"</SPAN>
<SPAN class="string token">"'Max X value for extent' <None> VISIBLE NONE;"</SPAN>
<SPAN class="string token">"'Min Y value for extent' <None> VISIBLE NONE;"</SPAN>
<SPAN class="string token">"'Max Y value for extent' <None> VISIBLE NONE;"</SPAN>
<SPAN class="string token">"'Additional Field' <None> VISIBLE NONE;"</SPAN>
<SPAN class="string token">"'Altname JoinID' <None> VISIBLE NONE"</SPAN>
<SPAN class="punctuation token">)</SPAN>
arcpy<SPAN class="punctuation token">.</SPAN>CreateAddressLocator_geocoding<SPAN class="punctuation token">(</SPAN>
<SPAN class="string token">"US Address - Single House"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="comment token">## in_address_locator_style</SPAN>
<SPAN class="string token">"'{}' 'Primary Table'"</SPAN><SPAN class="punctuation token">.</SPAN>format<SPAN class="punctuation token">(</SPAN>my_fancy_source_data<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="comment token">## in_reference_data</SPAN>
field_info<SPAN class="punctuation token">,</SPAN> <SPAN class="comment token">## in_field_map</SPAN>
temp_locator
<SPAN class="punctuation token">)</SPAN>
<SPAN class="comment token"># Create and analyze service definition draft file</SPAN>
sddraft <SPAN class="operator token">=</SPAN> os<SPAN class="punctuation token">.</SPAN>path<SPAN class="punctuation token">.</SPAN>join<SPAN class="punctuation token">(</SPAN>temp_dir<SPAN class="punctuation token">,</SPAN> locator_name<SPAN class="operator token">+</SPAN><SPAN class="string token">".sddraft"</SPAN><SPAN class="punctuation token">)</SPAN>
sdd_analyze_result <SPAN class="operator token">=</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>CreateGeocodeSDDraft<SPAN class="punctuation token">(</SPAN>
temp_locator<SPAN class="punctuation token">,</SPAN> <SPAN class="comment token">## in_address_locator</SPAN>
sddraft<SPAN class="punctuation token">,</SPAN> <SPAN class="comment token">## out_sddraft</SPAN>
locator_name<SPAN class="punctuation token">,</SPAN> <SPAN class="comment token">## service_name</SPAN>
<SPAN class="string token">"FROM_CONNECTION_FILE"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="comment token">## server_type</SPAN>
agsconn<SPAN class="punctuation token">,</SPAN> <SPAN class="comment token">## connection_file_path</SPAN>
folder_name<SPAN class="operator token">=</SPAN><SPAN class="string token">"Geocoders"</SPAN><SPAN class="punctuation token">,</SPAN>
summary<SPAN class="operator token">=</SPAN><SPAN class="string token">"Created by Python script {}"</SPAN><SPAN class="punctuation token">.</SPAN>format<SPAN class="punctuation token">(</SPAN>
datetime<SPAN class="punctuation token">.</SPAN>datetime<SPAN class="punctuation token">.</SPAN>now<SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">.</SPAN>strftime<SPAN class="punctuation token">(</SPAN><SPAN class="string token">"%Y-%m-%d %H:%M:%S"</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">)</SPAN>
<SPAN class="comment token"># Inspect analyze result</SPAN>
<SPAN class="string token">"""result object of CreateGeocodeSDDraft is a dictionary of dictionaries structured like
{
'errors': {},
'messages': {},
'warnings': {
(u'Warning message', 9999): [],
(u'Warning message', 9999): []
}
}
"""</SPAN>
<SPAN class="comment token">## Check for error messages to create the issues list.</SPAN>
<SPAN class="comment token">## If there are no analyze errors, analyze_issues will be empty list []</SPAN>
analyze_issues <SPAN class="operator token">=</SPAN> sdd_analyze_result<SPAN class="punctuation token">.</SPAN>get<SPAN class="punctuation token">(</SPAN><SPAN class="string token">"errors"</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">.</SPAN>keys<SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="comment token">## Check for warning messages and add them to the issues list.</SPAN>
<SPAN class="comment token">## Only include those that aren't in the ignore list.</SPAN>
warn_ignore <SPAN class="operator token">=</SPAN> <SPAN class="punctuation token">[</SPAN>
<SPAN class="punctuation token">(</SPAN><SPAN class="string token">'Missing Summary in Item Description'</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">97</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">,</SPAN>
<SPAN class="punctuation token">(</SPAN><SPAN class="string token">'Missing Tags in Item Description'</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">98</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">,</SPAN>
<SPAN class="punctuation token">(</SPAN><SPAN class="string token">'Locator will be copied to the server'</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">24044</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">]</SPAN>
analyze_issues <SPAN class="operator token">+=</SPAN> <SPAN class="punctuation token">[</SPAN>
warn <SPAN class="keyword token">for</SPAN> warn <SPAN class="keyword token">in</SPAN> sdd_analyze_result<SPAN class="punctuation token">.</SPAN>get<SPAN class="punctuation token">(</SPAN><SPAN class="string token">"warnings"</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">.</SPAN>keys<SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="keyword token">if</SPAN> warn <SPAN class="operator token">not</SPAN> <SPAN class="keyword token">in</SPAN> warn_ignore
<SPAN class="punctuation token">]</SPAN>
<SPAN class="keyword token">if</SPAN> analyze_issues<SPAN class="punctuation token">:</SPAN>
<SPAN class="keyword token">raise</SPAN> Exception<SPAN class="punctuation token">(</SPAN>
<SPAN class="string token">"Analyzing the service definition draft revealed an issue.\n"</SPAN>
<SPAN class="string token">"{}"</SPAN><SPAN class="punctuation token">.</SPAN>format<SPAN class="punctuation token">(</SPAN>analyze_issues<SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">)</SPAN>
<SPAN class="keyword token">else</SPAN><SPAN class="punctuation token">:</SPAN>
<SPAN class="comment token"># Modify Service Definition Draft file to overwrite the existing service</SPAN>
sddTypeReplacement<SPAN class="punctuation token">(</SPAN>sddraft<SPAN class="punctuation token">)</SPAN>
<SPAN class="comment token"># Convert Service Definition Draft to a Service Definition file.</SPAN>
<SPAN class="comment token">## Staging compiles all the necessary information needed to publish</SPAN>
<SPAN class="comment token">## the GIS resource. If your data is not registered with the server,</SPAN>
<SPAN class="comment token">## the data will be added when the Service Definition Draft file is staged.</SPAN>
service_definition <SPAN class="operator token">=</SPAN> os<SPAN class="punctuation token">.</SPAN>path<SPAN class="punctuation token">.</SPAN>join<SPAN class="punctuation token">(</SPAN>temp_dir<SPAN class="punctuation token">,</SPAN> locator_name<SPAN class="operator token">+</SPAN><SPAN class="string token">".sd"</SPAN><SPAN class="punctuation token">)</SPAN>
arcpy<SPAN class="punctuation token">.</SPAN>StageService_server<SPAN class="punctuation token">(</SPAN>sddraft<SPAN class="punctuation token">,</SPAN> service_definition<SPAN class="punctuation token">)</SPAN>
<SPAN class="comment token"># Upload and publish service definition file as a service</SPAN>
arcpy<SPAN class="punctuation token">.</SPAN>server<SPAN class="punctuation token">.</SPAN>UploadServiceDefinition<SPAN class="punctuation token">(</SPAN>service_definition<SPAN class="punctuation token">,</SPAN> agsconn<SPAN class="punctuation token">)</SPAN>
<SPAN class="keyword token">return</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>GetMessages<SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="comment token">## end if analyze_issues</SPAN>
<SPAN class="comment token">## end with temp_dir</SPAN>
<SPAN class="keyword token">def</SPAN> <SPAN class="token function">sddTypeReplacement</SPAN><SPAN class="punctuation token">(</SPAN>service_definition_draft<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">:</SPAN>
<SPAN class="string token">"""Modifies a service definition draft file to overwrite an existing service
instead of the defaut to create a new service.
This function based on Esri example code for arcpy.UploadServiceDefinition_server()
Requires module xml.dom.minidom as DOM
Takes string input parameter of the path to a service definition draft file.
I did test overwriting a service without this function and it seems to
work fine. However, I kept it because Esri took the effort to post the
code example in the documentation and there must be a good reason.
Futher investigation may be helpful in understanding this.
"""</SPAN>
xml_doc <SPAN class="operator token">=</SPAN> DOM<SPAN class="punctuation token">.</SPAN>parse<SPAN class="punctuation token">(</SPAN>service_definition_draft<SPAN class="punctuation token">)</SPAN>
descriptions <SPAN class="operator token">=</SPAN> xml_doc<SPAN class="punctuation token">.</SPAN>getElementsByTagName<SPAN class="punctuation token">(</SPAN><SPAN class="string token">"Type"</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="keyword token">for</SPAN> desc <SPAN class="keyword token">in</SPAN> descriptions<SPAN class="punctuation token">:</SPAN>
<SPAN class="keyword token">if</SPAN> desc<SPAN class="punctuation token">.</SPAN>parentNode<SPAN class="punctuation token">.</SPAN>tagName <SPAN class="operator token">==</SPAN> <SPAN class="string token">"SVCManifest"</SPAN><SPAN class="punctuation token">:</SPAN>
<SPAN class="keyword token">if</SPAN> desc<SPAN class="punctuation token">.</SPAN>hasChildNodes<SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">:</SPAN>
desc<SPAN class="punctuation token">.</SPAN>firstChild<SPAN class="punctuation token">.</SPAN>data <SPAN class="operator token">=</SPAN> <SPAN class="string token">"esriServiceDefinitionType_Replacement"</SPAN>
<SPAN class="keyword token">with</SPAN> open<SPAN class="punctuation token">(</SPAN>service_definition_draft<SPAN class="punctuation token">,</SPAN> <SPAN class="string token">'w'</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="keyword token">as</SPAN> f<SPAN class="punctuation token">:</SPAN>
xml_doc<SPAN class="punctuation token">.</SPAN>writexml<SPAN class="punctuation token">(</SPAN>f<SPAN class="punctuation token">)</SPAN>
@contextmanager
<SPAN class="keyword token">def</SPAN> <SPAN class="token function">makeTempDir</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">:</SPAN>
<SPAN class="string token">"""Creates a temporary folder and returns the full path name.
Use in with statement to delete the folder and all contents on exit.
Requires contextlib contextmanager, shutil, and tempfile modules.
"""</SPAN>
temp_dir <SPAN class="operator token">=</SPAN> tempfile<SPAN class="punctuation token">.</SPAN>mkdtemp<SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="keyword token">try</SPAN><SPAN class="punctuation token">:</SPAN>
<SPAN class="keyword token">yield</SPAN> temp_dir
<SPAN class="keyword token">finally</SPAN><SPAN class="punctuation token">:</SPAN>
shutil<SPAN class="punctuation token">.</SPAN>rmtree<SPAN class="punctuation token">(</SPAN>temp_dir<SPAN class="punctuation token">)</SPAN>
<SPAN class="comment token"># Script start</SPAN>
<SPAN class="keyword token">if</SPAN> __name__ <SPAN class="operator token">==</SPAN> <SPAN class="string token">'__main__'</SPAN><SPAN class="punctuation token">:</SPAN>
main<SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>I will be also adapting this same process for rebuilding geocode services with a composite locator. As always, suggestions are welcome!
P.S.
When I got this vague error, I found it was caused by incorrect field names in the field info for the in_field_map parameter of arcpy.CreateAddressLocator_geocoding()
ERROR 000042: Failed to create the address locator.
Underlying DBMS error [ORA-00923: FROM keyword not found where expected]