Support for OGC GeoPackage specification in ArcGIS.

Have you heard about the OGC GeoPackage specification (  It is a newly minted OGC spec that defines GeoPackages for exchange and GeoPackage SQLite Extensions for direct use of vector geospatial features and/or tile matrix sets. Esri has actively participated in the spec activity from the very beginning. To this end, we were one of the very early adopters of the specification ( early support even before the spec was approved by OGC membership).

If you are curious about GeoPackages, here’s what you can do. At 10.2.1 or with 10.2.2 ArcGIS desktop, you can create an empty GeoPackage and populate the GeoPackage by copying feature data into it. At 10.2.1, we supported the draft version of the specification and at 10.2.2, the final version of the spec is supported. Currently we support only vector features, but with 10.3 we expect to extend support for raster tiles. One of the primary uses cases driving GeoPackage use is mobile support. Expect to see support for GeoPackage in runtime later this year.

So if you are a sqllite database aficionado and would like to test the waters with GeoPackage, here’s what you can do today with 10.2.1 or 10.2.2. You can use the included script to create a sample empty GeoPackage and then populate it with vector features. Use this GeoPackage as you would any other dataset. We have noticed that in some cases when navigating to a directory that contains GeoPackage (.gpkg) data, ArcCatalog/ArcMap does not display the file. Please review this KB article if you run into this issue.

Lance Shipman on the database team has been actively involved with this effort from the very beginning. Lance and I would welcome your feedback, as we at Esri continue to improve and extend GeoPackage support in 10.3.

Sample python script to create a GeoPackage.

import arcpy

# Set local variables
sqlite_database_path = ‘C:\data\example.gpkg’

# Execute CreateSQLiteDatabase, “GEOPACKAGE”)

This entry was posted in Analysis & Geoprocessing, Defense, Developer, Geodata, Mapping, Open Data, Python and tagged , , , , . Bookmark the permalink.

Leave a Reply


  1. rsarwas says:

    Use this GeoPackage as you would any other dataset.
    I’m using Desktop 10.2.1, and I’ve successfully created a GeoPackage and imported data (very cool!), but I cannot edit this data in ArcMap – The workspace containing this data cannot be edited. Is editing supported? Will it be supported in 10.2.2, or runtime?

    • lshipman says:

      Currently in ArcGIS Desktop, GeoPackage editing is supported via code. You can open an update or insert cursor and update data using ArcObjects. Edit sessions will be supported in a later release.

      Runtime will support GeoPackaging editing as part of the summer release.

      • ultraayla says:

        Just following up on this since it’s been about 6 months. It sounds like geopackages are distinct from sqlite spatial databases, but build on them? You noted that Edit sessions will be supported in a later release – do you know now if that will occur in 10.3? I’m trying to make some decisions on future directions for a large project and I feel like Esri has been hinting about deprecating the personal geodatabase for a while – and SQLite/Spatialite is a really attractive alternative now that support is being added – I just don’t want to jump the gun. Thanks!

        • lshipman says:

          GeoPackages are built on top of SQLite databases. Edit sessions will not be supported at 10.3, but will be in a future release. Personal Geodatabases are not being depreciated, but are not supported in any 64-bit applications (ArcGIS Server, 64-bit Background Geoprocessing, Pro). They will continue to be supported in ArcGIS Desktop, a 32-bit application. SQLite, SpatiaLite and Geopackages are databases, not geodatabases. They all support a simple feature model (points, lines, polygons). Annotation, Networks, Topologies, etc. are not supported. If you need geodatabase functionality you need to look at File Geodatabase.

      • cbennettmr says:

        Is it possible to open a GeoPackage in .NET as a workspace? If so, could you post an example?

        • Eric Bader says:

          Are you asking, from ArcObjects .NET API, or from ArcGIS Runtime SDK for .NET?
          For ArcGIS Runtime SDK API for .NET, GeoPackage is not supported yet.
          As for ArcObjects .NET, I don’t believe you can open Geopackage.

          Does this help?

          • cbennettmr says:

            To be honest, I’m not sure of the difference between the runtime and the API?

            But I was able to get it to work after I landed on a clue. I searched the registry for “workspacefactory” (Win8) and found the SqLite component. Here is what I am using now in my test library (C#).

            string gpkgFile = @"C:\temp\Sample.gpkg";
            Type factoryType = Type.GetTypeFromProgID("esriDataSourcesGDB.SqliteWorkspaceFactory");
            var wkspFactory = (IWorkspaceFactory)Activator.CreateInstance(factoryType);
            IPropertySet propertySet = new PropertySetClass();
            propertySet.SetProperty("DATABASE", gpkgFile);
            propertySet.SetProperty("DBCLIENT", "gpkg");
            IWorkspace ws = ((IWorkspaceFactory2)wsf).Open(propertySet, 0);
            // Good to go!

            I can create tables, and fill rows with rowbuffer and insert cursor, so I expect most other calls to work.

  2. hawatson says:

    I’ve been able to use your code to create a new GeoPackage, and after removing my ArcCatalog cache files I can see it in ArcCatalog.

    Can you tell us what feature types are supported for copying to the new GeoPackage Geodatabase? Using default inputs to arcpy.ListFeatureClasses() I may get any of these below returned. I’m assuming only simple feature classes, but if you can clarify what is accepted, that would be great.

    Annotation —Only annotation feature classes are returned.
    Arc —Only arc (or line) feature classes are returned.
    Dimension —Only dimension feature classes are returned.
    Edge —Only edge feature classes are returned.
    Junction —Only junction feature classes are returned.
    Label — Only label feature classes are returned.
    Line —Only line (or arc) feature classes are returned.
    Multipatch —Only multipatch feature classes are returned.
    Node —Only node feature classes are returned.
    Point —Only point feature classes are returned.
    Polygon —Only polygon feature classes are returned.
    Polyline —Only line (or arc) feature classes are returned.
    Region —Only region feature classes are returned.
    Route —Only route feature classes are returned.
    Tic —Only tic feature classes are returned.
    All — All datasets in the workspace. This is the default value.

  3. kimo says:

    I have had great success in loading data into sqlite directly with a Python script. It is very fast and more flexible than a file geodatabase. For example one cursor will handle all the tables in a single database, no need to have a cursor per table. It also accepts compatible field types like a spreadsheet eg a floating point into a nominal integer field.
    Here is my script

    # Name:
    # Purpose: Load Stats Group CSV dump into sqlite
    # groups: Dwelling, Family, Household,
    # Individual-part-1,2,3a,3b
    # Author: kimo
    # Created: 05/04/2014, tidy up 24/04/2014
    # Copyright: (c) Kim Ollivier 2014
    # Licence: Creative Commons 3.0 NZ
    import sqlite3
    import os, sys, csv, re
    import datetime

    def load_metadata(metaTab,lstItem):
    '''Take the first line of the CSV
    write out original field names to metadata file
    for later reference by column number
    print '{} {} fields (zero based)'.format(metaTab,len(lstItem))
    c = conn.cursor()
    c.execute('''DROP TABLE IF EXISTS '''+ metaTab)
    c.execute('''CREATE TABLE '''+metaTab+''' (
    colID text NOT NULL,
    colValue text NOT NULL
    cmd = "INSERT INTO "+metaTab+"(colID,colValue) VALUES (?,?)"
    n = 0
    for fld in lstItem:
    trow = ('col'+str(n),unicode(fld,errors='ignore'))
    # print trow
    # print metaTab,"created"

    def create_dwelling_table(conn,templateTable):
    Create a table from scratch better than using
    a template, according to manual
    for lstItem in csv.reader(open(fileCSV, "rb")):
    n = len(lstItem)
    # print n,"fields, zero based"
    c = conn.cursor()
    c.execute('''DROP TABLE IF EXISTS '''+ templateTable)
    # floating point will be accepted into integer fields yay
    # because Dwellings have some mean values
    # might cause a problem later with other databases
    lstCol = []
    for id in range(3,len(lstItem)):
    fn = 'col'+str(id)
    lstCol.append(" {} integer,\n".format(fn))
    schemaCol = ' '.join(lstCol)[0:-2]
    c.execute('''CREATE TABLE '''+templateTable+''' (
    col0 text NOT NULL,
    col1 text NOT NULL,
    col2 text ,'''+

    def create_comment_table(conn,commentTable):
    # make up a comment template
    c = conn.cursor()
    c.execute('''DROP TABLE IF EXISTS '''+ commentTable)
    c.execute('''CREATE TABLE '''+commentTable+''' (
    col0 text NOT NULL,
    col1 text NOT NULL,
    col2 text )''')

    def insert_record(outTab,lstItem):
    '''load any table
    requires a database to be connected
    and a cursor to be open
    but the cool thing is the cursor is for all tables'''
    buf = [None if i in ["..C","*"] else i for i in lstItem]
    cmd = "INSERT INTO " + outTab + " VALUES (" + (",?"*(len(lstItem)))[1:] + ")"
    # implied transaction opened
    except Exception,msg:
    print msg
    print buf
    print cmd

    def insert_comment_row(outTab,row):
    '''load comment table
    requires a database to be connected
    and a cursor to be open
    but the cool thing is the cursor is for any tables'''
    cmd = "INSERT INTO " + outTab + " VALUES (" + (",?"*(len(row)))[1:] + ")"
    # implied transaction opened
    except Exception,msg:
    print msg
    print row
    print cmd

    # ========================== main ===================================
    if __name__ == '__main__':
    folder = sys.argv[1]
    group = sys.argv[2]
    except :
    folder = "F:/data/census2013"
    group = "Dwelling"
    dGroup = {
    fileCSV = folder+"/2013-mb-dataset-Total-New-Zealand-"+dGroup[group]+".csv"

    start =
    if not os.path.exists(fileCSV):
    print "Source not found",fileCSV
    conn = sqlite3.connect(group+'.sqlite')
    print group,"sqlite database successfully opened"
    # regular expressions to separate groups, note included space in expression
    # precompiled patterns for reliable separation
    p = {}
    p['MB'] = re.compile("^MB ") # for 2 digit MB code at start of a line
    p['AU'] = re.compile("^[0-9]{6} ") # for 6 digit AU at start of a line
    p['WA'] = re.compile("^[0-9]{5} ") # for 5 digit WARD at start of a line
    p['TLA'] = re.compile("^[0-9]{3} ") # for 3 digit TLA at start of a line
    p['REG'] = re.compile("^[0-9]{2} ") # for 2 digit REG at start of a line
    p['CMB'] = re.compile("^CMB ") # for CMB flag at start of a line
    p['Total'] = re.compile("^Total ") # for Total at start of a line
    p['Comment'] = re.compile("^Footnotes|Symbols|Source") # comments at end
    r = 0
    e = 0
    # create separate output table for each grouping
    lstTab = ['MB','AU','WA','REG','TLA','CMB','Total']
    for tab in lstTab:
    outTab = group+tab
    # load all records in one pass into respective group table
    c = conn.cursor() # cursor applies to ALL tables!
    for lstItem in csv.reader(open(fileCSV, "rb")):
    if r == 1:
    # write header to Metadata table
    else :
    # group filter for each record type
    if p['MB'].match(lstItem[0]):
    elif p['AU'].match(lstItem[0]):
    elif p['WA'].match(lstItem[0]):
    elif p['TLA'].match(lstItem[0]):
    elif p['REG'].match(lstItem[0]):
    elif p['CMB'].match(lstItem[0]):
    elif p['Total'].match(lstItem[0]):
    elif p['Comment'].match(lstItem[0]):
    row = []
    # oops, one strange char corruption in one of the comments
    # but no 255 char field width limits in sqlite
    else :
    # save inserts, close cursor, close database
    report2 = '{} total rows {} skipped'.format(r,e)
    print report2
    print "Elapsed", - start
    print "Well Done"

    • kimo says:

      Aagh, even though I wrapped the script in code tags the formatter dropped the indents.
      Never mind, I will post a link when I publish my toolbox next week.

  4. lcatania says:

    Actually the second parameter should be “ST_GEOMETRY”, not “GEOPACKAGE”, otherwise the data is not recognized in ArcMap. ArcMap doesn’t complain about it, but it will show an empty attribute table and nothing visually. Attributes do show up in ArcCatalog, but no visual preview. And if you try to import feature classes into the geopackage, it will fail with error:

    DBMS table not found[table main.gpkg_geometry_columns has no column named geometry_type]
    Attribute column not found[table main.gpkg_geometry_columns has no column named geometry_type]

    I struggled to figure this issue out for a bit. Once the second parameter was changed to “ST_GEOMETRY”, all worked fine. Assume the “GEOPACKAGE” parameter may be from when this was first introduced in 10.1 and has since changed in 10.2.2.

    • lshipman says:

      The second parameter of what? The “GEOPACKAGE” parameter is required for creating a GeoPackage via the gp.CreateSQLiteDatabase tool. Creating a table requires a ST_GEOMETRY, we don’t currently identify a GEOPACKAGE geometry as different in ArcGIS Desktop. Can you post a code fragment? What version of ArcGIS are you using?

      • lcatania says:

        I am referring to the code snip from this blog entry., “GEOPACKAGE”)

        I initially used this as is to create an empty geopackage to write features to. I was able to write feature with no errors, but when displaying in ArcMap and ArcCatalog, it did not work as I described in my first post. I change it to:, “ST_GEOMETRY”)

        and it worked as described in my first post.

        I am using 10.2.2. I found the info at:
        which indicated to use “ST_GEOMETRY”.

        When I was having my initial problems with displaying data in ArcMap, I had also downloaded the Simple Sewers test data set from site just to test displaying data thinking I was doing something wrong when writing features to the geopackage but ran into the same issues in ArcMap and ArcCatalog that I described inmy first post.

  5. skutz says:

    Is there support within ArcEngine 10.3 for creating a raster dataset from a GeoPackage via ArcObjects?

    The code fragment below shows an attempt that was not successful.
    - ArcGIS Engine Runtime 10.3
    - Visual Studio 2013
    - Language: Visual C++

    Result: Attempting ->OpenFromFile() using a RasterWorkspaceFactory generates an HRESULT of E_FAIL and a NULL workspace pointer.
    IWorkspaceFactoryPtr pWorkspaceFactory = NULL;
    IWorkspacePtr pWorkspace = NULL;
    IEnumDatasetNamePtr pEnumDatasetName = NULL;
    HRESULT hr = S_OK;
    CString csGeoPackageFileName = _T(“C:\\GeoPackage\\Test_With_Rasters.gpkg”);

    hr = pWorkspaceFactory.CreateInstance(CLSID_RasterWorkspaceFactory);

    // The hr value returned is E_FAIL and pWorkspace is a NULL pointer.
    hr = pWorkspaceFactory->OpenFromFile(CComBSTR(csGeoPackageFileName), 0, &pWorkspace);

    // Get an enumerator for the DatasetNames in the workspace.
    // Not possible due to the NULL pWorkspace pointer above.
    hr = pWorkspace->get_DatasetNames(esriDTAny, &pEnumDatasetName);
    Any insights are appreciated.

    • lshipman says:

      There is no support for GeoPackage raster in ArcObjects. There will be a GP tool in ArcGIS 10.4 that allows the loading of jpeg and png images to a GeoPackage. Drawing support is through Make Raster Layer. This will be made available via a download for ArcGIS 10.3 and 10.3 sometime this Spring. ArcGIS 10.3 requires a patch (QFE-103-DTS-309104) for these tools to work correctly. The patch should be available by the end of April.

  6. osprey12 says:

    At 10.3 was support extended to raster tiles? If so is there any documentation on this?

  7. says:

    Any news of being able to edit vector data in ArcMap that is stored in a GeoPackage or have the ability to export it from the layer/data/export dialog into a GeoPackage? This still has the ability to be a great format to use like a shapefile was but now it’s not quite there.

    • lshipman says:

      Nothing so far for editing. We would certainly like to see it. We can look at adding export from layer/data/export dialog in the post 10.5.1/2.0 time frame.