How can I refresh metadata on geodatabase feature classes including thumbnails

359
5
Jump to solution
11-08-2019 08:39 AM
HarlanMarshall
New Contributor III
Our environment(in case it matters): Windows Server 2016 Standard, ArcGIS Desktop 10.6.1, SQL Server 2017 Standard, Python 2.7
We have an enterprise SQL Server geodatabase functioning as a read-only library of GIS data that is refreshed nightly with changes from a versioned geodatabase. When a feature class is updated, the feature class is replaced in the library geodatabase and metadata must be recreated for it there. We have lots of user created metadata for each feature class that is maintained in a SQL Server table. After the feature class is replaced in the library geodatabase we export the auto-generated metadata on the updated library feature class to an xml file, modify the exported xml with values from our metadata table, and import it back into the library feature class.
I've worked through the python code needed to do all that but I'm stuck on the issue of getting the thumbnail images from a folder of regularly updated jpeg files into the xml file for export to the library feature class metadata.
Can anyone please help with how this can be done programmatically without manually using the metadata editing tools in ArcCatalog one at a time. We refresh over a hundred feature classes every day.
0 Kudos
1 Solution

Accepted Solutions
HarlanMarshall
New Contributor III

For future coders looking for "one" solution to this problem, I'm placing a short version of my final python solution.

Thanks to Heidi Kristenson for help with setting the thumbnail in the xml.


Problem: Update metadata on a geodatabase feature class from metadata maintained in a separate table. Include a thumbnail from an already created jpeg file.


Solution: Export the metadata from the target feature class to a scratch XML file. Modify the xml file elements with the new metadata values as well as the thumbnail. Import the modified metadata back into the target geodatabase feature class.

Of course, our solution iterates through a list of geodatabase feature classes to update the metadata which this sample does not illustrate for simplicity.

import os
import arcpy
import xml.etree.ElementTree as ET
import base64
from pyodbc import connect as odbcconn
from pandas import read_sql_query as readsqlqry

num_elements = 0
featName = 'your_featureClassName'
fcPathName = 'Database Connections/your_geodatabase.sde/' + featName

# set up a connection to read user maintained metadata from a SQL Server table
metaConnStr = 'DRIVER={SQL Server};SERVER=servername;DATABASE=dbname;Trusted_Connection=yes'
conn = odbcconn(metaConnStr)
# your metadata source data will differ from the following
metaqry = 'SELECT [FULL_NAME],[COVER_NAME],[ABSTRACT],[UPDATEDATE],[OWNERNAME]' +\
',[PATH],[METAACCESS],[ONMAINT],[MAINTFREQ],[KNOWNERROR],[LINEAGE]' +\
',[DOMAIN],[RECTIFIED],[MAINTORG],[MAINTDESC],[LIBINPUT],[SOURCNAME]' +\
',[SOURCCONTACT],[SOURCDOCNAME],[SOURCDATE],[SOURCSCALE],[SOURCFORMAT]' +\
',[SOUR2NAME],[SOUR2CONTACT],[SOUR2DOCNAME],[SOUR2DATE],[SOUR2SCALE]' +\
',[SOUR2FORMAT],[ONMG],[MGLAYERNAME],[MGSCALELOW],[MGSCALEHIGH] ' +\
'FROM [dbo].[metadata] WHERE [COVER_NAME] = \'' + featName + '\''
df_FCMeta = readsqlqry(metaqry, conn) # load query result to pandas dataframe
df_row = df_FCMeta.iloc[0] # get the one row in the dataframe. There is only one row per COVER_NAME

arcpy.env.overwriteOutput = True
# export the ESRI generated metadata from the target feature class
# install location
dir = arcpy.GetInstallInfo('desktop')['InstallDir']

# stylesheet to use
copy_xslt = r'{0}'.format(os.path.join(dir,'Metadata\Stylesheets\gpTools\exact copy of.xslt'))

# temporary XML file
xmlfile = arcpy.CreateScratchName('.xml',workspace=arcpy.env.scratchFolder)

# export xml
arcpy.XSLTransform_conversion(fcPathName, copy_xslt, xmlfile, '')

# get the tree object and root element from the exported xml file
tree = ET.parse(xmlfile)
root = tree.getroot()

# get the dataIdInfo element
dataIdInfoEl = root.find('dataIdInfo')

# dataIdInfo purpose element
# Create the needed purpose xml element
subEl = ET.SubElement(dataIdInfoEl,'idPurp')
subEl.text = df_row['FULL_NAME']
num_elements += 1

# dataIdInfo abstract element
# Create the needed abstract xml element
subEl = ET.SubElement(dataIdInfoEl,'idAbs')
subEl.text = df_row['ABSTRACT']
num_elements += 1

# import the thumbnail image (jpg) from a file folder by name. Thumbnail file must exist (created by a separate process)
thumbnailsPath = 'c:/thumbnails/'
jpgFile = thumbnailsPath + featName + '.jpg'
if os.path.exists(jpgFile):
with open(jpgFile, "rb") as img_file:
strEncoded = base64.b64encode(img_file.read())
# Create the needed thumbnail xml element
attrib = {'EsriPropertyType':'PictureX'}
subEl = ET.SubElement(root,'Binary')
subEl = ET.SubElement(subEl,'Thumbnail')
subEl = ET.SubElement(subEl,'Data', attrib)
subEl.text = strEncoded
num_elements += 1

if num_elements > 0:
# save modifications to XML
tree.write(xmlfile)
arcpy.MetadataImporter_conversion(xmlfile, fcPathName)
else:
print('No changes to save')‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

View solution in original post

0 Kudos
5 Replies
DanPatterson_Retired
MVP Esteemed Contributor

There are no arcpy classes or functions at present, so you might want to jump on these for support

Arcpy metadata editing functions 

 

plus a few others

0 Kudos
HarlanMarshall
New Contributor III

Thanks for the links Dan.

I found the answer I was looking for here.

https://community.esri.com/message/890233-re-embed-a-thumbnail-in-xml-for-item-description?commentID=890233&et=watches.email.thread#comment-890233

Harlan Marshall

GIS Analyst – Senior

Pima County

(520) 724-6757

0 Kudos
irfanlatif
New Contributor III

I am using Arcpy Metadata Editor to update our metada in Shape Files and feature classes in Filegeodb format.

I am not sure it will work with SQL Server.

0 Kudos
HarlanMarshall
New Contributor III

Thanks for your answer. I was actually looking for a programmatic way to update metadata.

Harlan Marshall

GIS Analyst – Senior

Pima County

(520) 724-6757

0 Kudos
HarlanMarshall
New Contributor III

For future coders looking for "one" solution to this problem, I'm placing a short version of my final python solution.

Thanks to Heidi Kristenson for help with setting the thumbnail in the xml.


Problem: Update metadata on a geodatabase feature class from metadata maintained in a separate table. Include a thumbnail from an already created jpeg file.


Solution: Export the metadata from the target feature class to a scratch XML file. Modify the xml file elements with the new metadata values as well as the thumbnail. Import the modified metadata back into the target geodatabase feature class.

Of course, our solution iterates through a list of geodatabase feature classes to update the metadata which this sample does not illustrate for simplicity.

import os
import arcpy
import xml.etree.ElementTree as ET
import base64
from pyodbc import connect as odbcconn
from pandas import read_sql_query as readsqlqry

num_elements = 0
featName = 'your_featureClassName'
fcPathName = 'Database Connections/your_geodatabase.sde/' + featName

# set up a connection to read user maintained metadata from a SQL Server table
metaConnStr = 'DRIVER={SQL Server};SERVER=servername;DATABASE=dbname;Trusted_Connection=yes'
conn = odbcconn(metaConnStr)
# your metadata source data will differ from the following
metaqry = 'SELECT [FULL_NAME],[COVER_NAME],[ABSTRACT],[UPDATEDATE],[OWNERNAME]' +\
',[PATH],[METAACCESS],[ONMAINT],[MAINTFREQ],[KNOWNERROR],[LINEAGE]' +\
',[DOMAIN],[RECTIFIED],[MAINTORG],[MAINTDESC],[LIBINPUT],[SOURCNAME]' +\
',[SOURCCONTACT],[SOURCDOCNAME],[SOURCDATE],[SOURCSCALE],[SOURCFORMAT]' +\
',[SOUR2NAME],[SOUR2CONTACT],[SOUR2DOCNAME],[SOUR2DATE],[SOUR2SCALE]' +\
',[SOUR2FORMAT],[ONMG],[MGLAYERNAME],[MGSCALELOW],[MGSCALEHIGH] ' +\
'FROM [dbo].[metadata] WHERE [COVER_NAME] = \'' + featName + '\''
df_FCMeta = readsqlqry(metaqry, conn) # load query result to pandas dataframe
df_row = df_FCMeta.iloc[0] # get the one row in the dataframe. There is only one row per COVER_NAME

arcpy.env.overwriteOutput = True
# export the ESRI generated metadata from the target feature class
# install location
dir = arcpy.GetInstallInfo('desktop')['InstallDir']

# stylesheet to use
copy_xslt = r'{0}'.format(os.path.join(dir,'Metadata\Stylesheets\gpTools\exact copy of.xslt'))

# temporary XML file
xmlfile = arcpy.CreateScratchName('.xml',workspace=arcpy.env.scratchFolder)

# export xml
arcpy.XSLTransform_conversion(fcPathName, copy_xslt, xmlfile, '')

# get the tree object and root element from the exported xml file
tree = ET.parse(xmlfile)
root = tree.getroot()

# get the dataIdInfo element
dataIdInfoEl = root.find('dataIdInfo')

# dataIdInfo purpose element
# Create the needed purpose xml element
subEl = ET.SubElement(dataIdInfoEl,'idPurp')
subEl.text = df_row['FULL_NAME']
num_elements += 1

# dataIdInfo abstract element
# Create the needed abstract xml element
subEl = ET.SubElement(dataIdInfoEl,'idAbs')
subEl.text = df_row['ABSTRACT']
num_elements += 1

# import the thumbnail image (jpg) from a file folder by name. Thumbnail file must exist (created by a separate process)
thumbnailsPath = 'c:/thumbnails/'
jpgFile = thumbnailsPath + featName + '.jpg'
if os.path.exists(jpgFile):
with open(jpgFile, "rb") as img_file:
strEncoded = base64.b64encode(img_file.read())
# Create the needed thumbnail xml element
attrib = {'EsriPropertyType':'PictureX'}
subEl = ET.SubElement(root,'Binary')
subEl = ET.SubElement(subEl,'Thumbnail')
subEl = ET.SubElement(subEl,'Data', attrib)
subEl.text = strEncoded
num_elements += 1

if num_elements > 0:
# save modifications to XML
tree.write(xmlfile)
arcpy.MetadataImporter_conversion(xmlfile, fcPathName)
else:
print('No changes to save')‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

View solution in original post

0 Kudos