private void updateSource(ITable sourceTable, string action, long oldOSMFeatureID, long newOSMFeatureID, string userName, int userID, int osmVersion, int changeSetID, Dictionary<long, long> nodeIDLookup, Dictionary<long, long> wayIDLookup, Dictionary<long, long> relationIDLookup, int extensionVersion)
        {
            IOSMClassExtension osmExtension = sourceTable.Extension as IOSMClassExtension;

            try
            {
                if (osmExtension != null)
                {
                    osmExtension.CanBypassChangeDetection = true;
                }

                // let's find all the matching row with the old osm feature ID
                IQueryFilter queryFilter = new QueryFilterClass();
                queryFilter.WhereClause = sourceTable.WhereClauseByExtensionVersion(oldOSMFeatureID, "osmID", extensionVersion);

                using (ComReleaser comReleaser = new ComReleaser())
                {
                    ICursor updateCursor = sourceTable.Update(queryFilter, false);
                    comReleaser.ManageLifetime(updateCursor);

                    IRow updateRow = updateCursor.NextRow();
                    comReleaser.ManageLifetime(updateRow);

                    int osmIDFieldIndex = sourceTable.Fields.FindField("osmID");
                    int osmUserFieldIndex = sourceTable.Fields.FindField("osmuser");
                    int osmUIDFieldIndex = sourceTable.Fields.FindField("osmuid");
                    int osmVersionFieldIndex = sourceTable.Fields.FindField("osmversion");
                    int osmVisibleFieldIndex = sourceTable.Fields.FindField("osmvisible");
                    int osmChangesetIDFieldIndex = sourceTable.Fields.FindField("osmchangeset");
                    int osmTimeStampFieldIndex = sourceTable.Fields.FindField("osmtimestamp");
                    int osmMembersFieldIndex = sourceTable.FindField("osmMembers");
                    int osmisMemberOfFieldIndex = sourceTable.FindField("osmMemberOf");
                    int trackChangesFieldIndex = sourceTable.FindField("osmTrackChanges");

                    if (updateRow != null)
                    {
                        if (osmIDFieldIndex > -1)
                        {
                            if (extensionVersion == 1)
                                updateRow.set_Value(osmIDFieldIndex, Convert.ToInt32(newOSMFeatureID));
                            else if (extensionVersion == 2)
                                updateRow.set_Value(osmIDFieldIndex, Convert.ToString(newOSMFeatureID));
                        }
                        if (osmUserFieldIndex > -1)
                        {
                            updateRow.set_Value(osmUserFieldIndex, userName);
                        }
                        if (osmUIDFieldIndex > -1)
                        {
                            updateRow.set_Value(osmUIDFieldIndex, userID);
                        }
                        if (osmVisibleFieldIndex > -1)
                        {
                            updateRow.set_Value(osmVisibleFieldIndex, "true");
                        }
                        if (osmChangesetIDFieldIndex > -1)
                        {
                            updateRow.set_Value(osmChangesetIDFieldIndex, changeSetID);
                        }
                        if (osmTimeStampFieldIndex > -1)
                        {
                            updateRow.set_Value(osmTimeStampFieldIndex, DateTime.Now.ToUniversalTime());
                        }
                        if (trackChangesFieldIndex > -1)
                        {
                            updateRow.set_Value(trackChangesFieldIndex, 1);
                        }

                        // if we are dealing with a row with a shape field (a feature), then change the IDs of the geometry as well
                        IFeature updateFeature = updateRow as IFeature;
                        if (updateFeature != null)
                        {
                            if (updateFeature.Shape.IsEmpty == false)
                            {
                                switch (updateFeature.Shape.GeometryType)
                                {
                                    case esriGeometryType.esriGeometryAny:
                                        break;
                                    case esriGeometryType.esriGeometryBag:
                                        break;
                                    case esriGeometryType.esriGeometryBezier3Curve:
                                        break;
                                    case esriGeometryType.esriGeometryCircularArc:
                                        break;
                                    case esriGeometryType.esriGeometryEllipticArc:
                                        break;
                                    case esriGeometryType.esriGeometryEnvelope:
                                        break;
                                    case esriGeometryType.esriGeometryLine:
                                        break;
                                    case esriGeometryType.esriGeometryMultiPatch:
                                        break;
                                    case esriGeometryType.esriGeometryMultipoint:
                                        break;
                                    case esriGeometryType.esriGeometryNull:
                                        break;
                                    case esriGeometryType.esriGeometryPath:
                                        break;
                                    case esriGeometryType.esriGeometryPoint:
                                        IPoint featurePoint = updateFeature.Shape as IPoint;
                                        if (extensionVersion == 1)
                                            featurePoint.ID = Convert.ToInt32(newOSMFeatureID);
                                        else if (extensionVersion == 2)
                                            // nothing required as the ObjectID of there referenced feature doesn't change
                                            updateFeature.Shape = featurePoint;
                                        if (featurePoint != null)
                                            ComReleaser.ReleaseCOMObject(featurePoint);
                                        break;
                                    case esriGeometryType.esriGeometryPolygon:
                                        IPointCollection pointCollection = updateFeature.Shape as IPointCollection;
                                        IEnumVertex enumVertex = pointCollection.EnumVertices;
                                        enumVertex.Reset();

                                        IPoint currentPoint = null;
                                        int vertexIndex = -1;
                                        int partIndex = -1;

                                        enumVertex.Next(out currentPoint, out partIndex, out vertexIndex);

                                        while (currentPoint != null)
                                        {
                                            if (extensionVersion == 1)
                                            {
                                                if (nodeIDLookup.ContainsKey(currentPoint.ID))
                                                {
                                                    enumVertex.put_ID(Convert.ToInt32(nodeIDLookup[currentPoint.ID]));
                                                }
                                            }
                                            else if (extensionVersion == 2)
                                            {
                                                // nothing required as the ObjectIDs of referenced features don't change
                                            }
                                            enumVertex.Next(out currentPoint, out partIndex, out vertexIndex);
                                        }
                                        updateFeature.Shape = (IGeometry)pointCollection;

                                        // if we are dealing with a multi-part feature then we will need to referenced members as well

                                        break;
                                    case esriGeometryType.esriGeometryPolyline:
                                        pointCollection = updateFeature.Shape as IPointCollection;
                                        enumVertex = pointCollection.EnumVertices;
                                        enumVertex.Reset();

                                        currentPoint = null;
                                        vertexIndex = -1;
                                        partIndex = -1;

                                        enumVertex.Next(out currentPoint, out partIndex, out vertexIndex);

                                        while (currentPoint != null)
                                        {
                                            if (extensionVersion == 1)
                                            {
                                                if (nodeIDLookup.ContainsKey(currentPoint.ID))
                                                {
                                                    enumVertex.put_ID(Convert.ToInt32(nodeIDLookup[currentPoint.ID]));
                                                }
                                            }
                                            else if (extensionVersion == 2)
                                            {
                                                // nothing required as the ObjectIDs of referenced features don't change
                                            }
                                            enumVertex.Next(out currentPoint, out partIndex, out vertexIndex);
                                        }
                                        updateFeature.Shape = (IGeometry)pointCollection;

                                        // if we are dealing with a multi-part feature then we will need to referenced members as well

                                        break;
                                    case esriGeometryType.esriGeometryRay:
                                        break;
                                    case esriGeometryType.esriGeometryRing:
                                        break;
                                    case esriGeometryType.esriGeometrySphere:
                                        break;
                                    case esriGeometryType.esriGeometryTriangleFan:
                                        break;
                                    case esriGeometryType.esriGeometryTriangleStrip:
                                        break;
                                    case esriGeometryType.esriGeometryTriangles:
                                        break;
                                    default:
                                        break;
                                }
                            }
                        }

                        if (osmisMemberOfFieldIndex > -1)
                        {
                            List<string> isMemberOfList = _osmUtility.retrieveIsMemberOf(updateRow, osmisMemberOfFieldIndex);

                            if (isMemberOfList != null && isMemberOfList.Count > 0)
                            {
                                List<string> newIsMemberOfList = new List<string>();

                                if (((IDataset)sourceTable).Name.Contains("_osm_ln") || ((IDataset)sourceTable).Name.Contains("_osm_ply"))
                                {
                                    // since version 1.1 is only dealing with homogeneous features  - lines/polygons
                                    // or in other words only ways we only go through the way look up at this point
                                    foreach (string parentString in isMemberOfList)
                                    {
                                        long idToCheck = Convert.ToInt64(parentString);
                                        if (wayIDLookup.ContainsKey(idToCheck))
                                        {
                                            newIsMemberOfList.Add(wayIDLookup[idToCheck].ToString());
                                        }
                                        else
                                        {
                                            newIsMemberOfList.Add(parentString);
                                        }
                                    }
                                }
                                else
                                {
                                    // in case we dealing with a relation the entity is assumed to be a hybrid and we need to check against node, way, and relations
                                    foreach (string parentString in isMemberOfList)
                                    {
                                        long idToCheck = Convert.ToInt64(parentString);
                                        if (wayIDLookup.ContainsKey(idToCheck))
                                        {
                                            idToCheck = wayIDLookup[idToCheck];
                                        }

                                        if (nodeIDLookup.ContainsKey(idToCheck))
                                        {
                                            idToCheck = nodeIDLookup[idToCheck];
                                        }

                                        if (relationIDLookup.ContainsKey(idToCheck))
                                        {
                                            idToCheck = relationIDLookup[idToCheck];
                                        }

                                        newIsMemberOfList.Add(idToCheck.ToString());
                                    }
                                }

                                _osmUtility.insertIsMemberOf(osmisMemberOfFieldIndex, newIsMemberOfList, updateRow);
                            }
                        }

                        if (osmMembersFieldIndex > -1)
                        {
                            member[] members = _osmUtility.retrieveMembers(updateRow, osmMembersFieldIndex);

                            if (members != null)
                            {
                                for (int memberIndex = 0; memberIndex < members.Length; memberIndex++)
                                {
                                    switch (members[memberIndex].type)
                                    {
                                        case memberType.way:
                                            long wayID = Convert.ToInt64(members[memberIndex].@ref);
                                            if (wayIDLookup.ContainsKey(wayID))
                                            {
                                                members[memberIndex].@ref = wayIDLookup[wayID].ToString();
                                            }
                                            break;
                                        case memberType.node:
                                            long nodeID = Convert.ToInt64(members[memberIndex].@ref);
                                            if (nodeIDLookup.ContainsKey(nodeID))
                                            {
                                                members[memberIndex].@ref = nodeIDLookup[nodeID].ToString();
                                            }
                                            break;
                                        case memberType.relation:
                                            long relationID = Convert.ToInt64(members[memberIndex].@ref);
                                            if (relationIDLookup.ContainsKey(relationID))
                                            {
                                                members[memberIndex].@ref = relationIDLookup[relationID].ToString();
                                            }
                                            break;
                                        default:
                                            break;
                                    }
                                }

                                _osmUtility.insertMembers(osmMembersFieldIndex, updateRow, members);
                            }
                        }

                        if (osmVersionFieldIndex > -1)
                        {
                            updateRow.set_Value(osmVersionFieldIndex, osmVersion);
                        }

                        updateCursor.UpdateRow(updateRow);
                    }
                }
            }
            catch { }
            finally
            {
                if (osmExtension != null)
                    osmExtension.CanBypassChangeDetection = false;
            }
        }
        private void ParseResultDiff(diffResult diffResultResponse, ITable revisionTable, IFeatureClass pointFeatureClass, IFeatureClass lineFeatureClass, IFeatureClass polygonFeatureClass, ITable relationTable, string userName, int userID, string changetSetID, Dictionary<long, long> nodeIDLookup, Dictionary<long, long> wayIDLookup, Dictionary<long, long> relationIDLookup, int extensionVersion)
        {
            if (diffResultResponse == null)
                return;

            if (diffResultResponse.Items == null)
                return;

            // let's find all the matching row with the old osm feature ID
            IQueryFilter queryFilter = new QueryFilterClass();

            int sourceFCNameFieldIndex = revisionTable.Fields.FindField("sourcefcname");
            int statusFieldIndex = revisionTable.Fields.FindField("osmstatus");
            int statusCodeFieldIndex = revisionTable.Fields.FindField("osmstatuscode");
            int newIDFieldIndex = revisionTable.Fields.FindField("osmnewid");
            int osmVersionFieldIndex = revisionTable.Fields.FindField("osmversion");
            int changesetIDFieldIndex = revisionTable.Fields.FindField("osmchangeset");

            SQLFormatter sqlFormatter = new SQLFormatter(revisionTable);

            foreach (var item in diffResultResponse.Items)
            {
                if (item is diffResultNode)
                {
                    diffResultNode diffNode = item as diffResultNode;
                    long newID = -1;
                    int newVersion = -1;
                    long oldID = -1;

                    long.TryParse(diffNode.new_id, out newID);
                    int.TryParse(diffNode.new_version, out newVersion);
                    long.TryParse(diffNode.old_id, out oldID);

                    //
                    if (nodeIDLookup.ContainsKey(oldID) == false)
                    {
                        nodeIDLookup.Add(oldID, newID);
                    }

                    updateSource((ITable)pointFeatureClass, determineAction(diffNode), Convert.ToInt64(diffNode.old_id), newID, userName, userID, newVersion, Convert.ToInt32(changetSetID), nodeIDLookup, wayIDLookup, relationIDLookup, extensionVersion);

                    queryFilter.WhereClause = revisionTable.WhereClauseByExtensionVersion(diffNode.old_id, "osmoldid", extensionVersion)
                        + " AND " + sqlFormatter.SqlIdentifier("osmelementtype") + " = 'node'" + " AND " + sqlFormatter.SqlIdentifier("osmstatuscode") + " IS NULL"; ;

                    using (ComReleaser comReleaser = new ComReleaser())
                    {
                        ICursor updateCursor = revisionTable.Update(queryFilter, false);
                        comReleaser.ManageLifetime(updateCursor);

                        IRow currentRow = updateCursor.NextRow();

                        if (currentRow == null)
                            continue;

                        if (statusFieldIndex > -1)
                        {
                            currentRow.set_Value(statusFieldIndex, HttpStatusCode.OK.ToString());
                        }

                        if (statusCodeFieldIndex > -1)
                        {
                            currentRow.set_Value(statusCodeFieldIndex, (int)HttpStatusCode.OK);
                        }

                        if (newIDFieldIndex > -1)
                        {
                            currentRow.set_Value(newIDFieldIndex, newID);
                        }

                        if (osmVersionFieldIndex > -1)
                        {
                            currentRow.set_Value(osmVersionFieldIndex, newVersion);
                        }

                        if (changesetIDFieldIndex > -1)
                        {
                            currentRow.set_Value(changesetIDFieldIndex, changetSetID);
                        }

                        updateCursor.UpdateRow(currentRow);
                    }
                }
                else if (item is diffResultWay)
                {
                    diffResultWay diffWay = item as diffResultWay;
                    queryFilter.WhereClause = revisionTable.WhereClauseByExtensionVersion(diffWay.old_id, "osmoldid", extensionVersion)
                        + " AND " + sqlFormatter.SqlIdentifier("osmelementtype") + " = 'way'" + " AND " + sqlFormatter.SqlIdentifier("osmstatuscode") + " IS NULL";

                    long newID = -1;
                    int newVersion = -1;
                    long oldID = -1;

                    long.TryParse(diffWay.new_id, out newID);
                    int.TryParse(diffWay.new_version, out newVersion);
                    long.TryParse(diffWay.old_id, out oldID);

                    //
                    if (wayIDLookup.ContainsKey(oldID) == false)
                    {
                        wayIDLookup.Add(oldID, newID);
                    }

                    using (ComReleaser comReleaser = new ComReleaser())
                    {
                        ICursor updateCursor = revisionTable.Update(queryFilter, false);
                        comReleaser.ManageLifetime(updateCursor);

                        IRow currentRow = updateCursor.NextRow();

                        if (currentRow == null)
                            continue;

                        string sourceFCName = String.Empty;
                        if (sourceFCNameFieldIndex > -1)
                        {
                            sourceFCName = currentRow.get_Value(sourceFCNameFieldIndex) as string;
                        }

                        if (string.IsNullOrEmpty(sourceFCName))
                            continue;

                        if (sourceFCName.Contains("_osm_ln"))
                        {
                            updateSource((ITable)lineFeatureClass, determineAction(diffWay), Convert.ToInt64(diffWay.old_id), newID, userName, userID, newVersion, Convert.ToInt32(changetSetID), nodeIDLookup, wayIDLookup, relationIDLookup, extensionVersion);
                        }
                        else if (sourceFCName.Contains("_osm_ply"))
                        {
                            updateSource((ITable)polygonFeatureClass, determineAction(diffWay), Convert.ToInt64(diffWay.old_id), newID, userName, userID, newVersion, Convert.ToInt32(changetSetID), nodeIDLookup, wayIDLookup, relationIDLookup, extensionVersion);
                        }

                        if (statusFieldIndex > -1)
                        {
                            currentRow.set_Value(statusFieldIndex, HttpStatusCode.OK.ToString());
                        }

                        if (statusCodeFieldIndex > -1)
                        {
                            currentRow.set_Value(statusCodeFieldIndex, (int)HttpStatusCode.OK);
                        }

                        if (newIDFieldIndex > -1)
                        {
                            currentRow.set_Value(newIDFieldIndex, newID);
                        }

                        if (osmVersionFieldIndex > -1)
                        {
                            currentRow.set_Value(osmVersionFieldIndex, newVersion);
                        }

                        if (changesetIDFieldIndex > -1)
                        {
                            currentRow.set_Value(changesetIDFieldIndex, changetSetID);
                        }

                        updateCursor.UpdateRow(currentRow);
                    }
                }
                else if (item is diffResultRelation)
                {
                    diffResultRelation diffRelation = item as diffResultRelation;
                    queryFilter.WhereClause = revisionTable.WhereClauseByExtensionVersion(diffRelation.old_id, "osmoldid", extensionVersion)
                        + " AND " + sqlFormatter.SqlIdentifier("osmelementtype") + " = 'relation'" + " AND " + sqlFormatter.SqlIdentifier("osmstatuscode") + " IS NULL"; ;

                    long newID = -1;
                    int newVersion = -1;
                    long oldID = -1;

                    long.TryParse(diffRelation.new_id, out newID);
                    int.TryParse(diffRelation.new_version, out newVersion);
                    long.TryParse(diffRelation.old_id, out oldID);

                    //
                    if (relationIDLookup.ContainsKey(oldID) == false)
                    {
                        relationIDLookup.Add(oldID, newID);
                    }

                    using (ComReleaser comReleaser = new ComReleaser())
                    {
                        ICursor updateCursor = revisionTable.Update(queryFilter, false);
                        comReleaser.ManageLifetime(updateCursor);

                        IRow currentRow = updateCursor.NextRow();

                        if (currentRow == null)
                            continue;

                        string sourceFCName = String.Empty;
                        if (sourceFCNameFieldIndex > -1)
                        {
                            sourceFCName = currentRow.get_Value(sourceFCNameFieldIndex) as string;
                        }

                        if (string.IsNullOrEmpty(sourceFCName))
                            continue;

                        if (sourceFCName.Contains("_osm_ln"))
                        {
                            updateSource((ITable)lineFeatureClass, determineAction(diffRelation), Convert.ToInt64(diffRelation.old_id), newID, userName, userID, newVersion, Convert.ToInt32(changetSetID), nodeIDLookup, wayIDLookup, relationIDLookup, extensionVersion);
                        }
                        else if (sourceFCName.Contains("_osm_ply"))
                        {
                            updateSource((ITable)polygonFeatureClass, determineAction(diffRelation), Convert.ToInt64(diffRelation.old_id), newID, userName, userID, newVersion, Convert.ToInt32(changetSetID), nodeIDLookup, wayIDLookup, relationIDLookup, extensionVersion);
                        }
                        else if (sourceFCName.Contains("_osm_relation"))
                        {
                            updateSource(relationTable, determineAction(diffRelation), Convert.ToInt64(diffRelation.old_id), newID, userName, userID, newVersion, Convert.ToInt32(changetSetID), nodeIDLookup, wayIDLookup, relationIDLookup, extensionVersion);
                        }

                        if (statusFieldIndex > -1)
                        {
                            currentRow.set_Value(statusFieldIndex, HttpStatusCode.OK.ToString());
                        }

                        if (statusCodeFieldIndex > -1)
                        {
                            currentRow.set_Value(statusCodeFieldIndex, (int)HttpStatusCode.OK);
                        }

                        if (newIDFieldIndex > -1)
                        {
                            currentRow.set_Value(newIDFieldIndex, newID);
                        }

                        if (osmVersionFieldIndex > -1)
                        {
                            currentRow.set_Value(osmVersionFieldIndex, newVersion);
                        }

                        if (changesetIDFieldIndex > -1)
                        {
                            currentRow.set_Value(changesetIDFieldIndex, changetSetID);
                        }

                        updateCursor.UpdateRow(currentRow);
                    }
                }
            }
        }
        private osmRelationGeometryType walkRelationGeometry(IFeatureClass osmLineFeatureClass, IFeatureClass osmPolygonFeatureClass, ITable relationTable, relation currentRelation)
        {
            osmRelationGeometryType testedGeometry = osmRelationGeometryType.osmUnknownGeometry;

            // we use this dictionary to determine if we can fully walk this relation
            // - the assumption is that we can walk the geometry is all node counts are 2
            Dictionary<int, int> nodeCountDictionary = new Dictionary<int, int>();

            try
            {
                if (currentRelation.Items == null)
                    return testedGeometry;

                foreach (var item in currentRelation.Items)
                {
                    if (item is ESRI.ArcGIS.OSM.OSMClassExtension.member)
                    {
                        member memberItem = item as member;
                        IQueryFilter osmIDQueryFilter = new QueryFilterClass();

                        using (ComReleaser comReleaser = new ComReleaser())
                        {
                            if (osmLineFeatureClass != null)
                            {
                                osmIDQueryFilter.WhereClause = osmLineFeatureClass.WhereClauseByExtensionVersion(memberItem.@ref, "OSMID", 2);
                                IFeatureCursor lineFeatureCursor = osmLineFeatureClass.Search(osmIDQueryFilter, false);
                                comReleaser.ManageLifetime(lineFeatureCursor);

                                IFeature foundLineFeature = lineFeatureCursor.NextFeature();

                                if (foundLineFeature != null)
                                {
                                    IPointCollection pointCollection = foundLineFeature.Shape as IPointCollection;

                                    int firstPointID = pointCollection.get_Point(0).ID;
                                    int lastPointID = pointCollection.get_Point(pointCollection.PointCount - 1).ID;

                                    if (nodeCountDictionary.ContainsKey(firstPointID))
                                    {
                                        nodeCountDictionary[firstPointID] = nodeCountDictionary[firstPointID] + 1;
                                    }
                                    else
                                    {
                                        nodeCountDictionary.Add(firstPointID, 1);
                                    }

                                    if (nodeCountDictionary.ContainsKey(lastPointID))
                                    {
                                        nodeCountDictionary[lastPointID] = nodeCountDictionary[lastPointID] + 1;
                                    }
                                    else
                                    {
                                        nodeCountDictionary.Add(lastPointID, 1);
                                    }
                                }

                                osmIDQueryFilter.WhereClause = osmPolygonFeatureClass.WhereClauseByExtensionVersion(memberItem.@ref, "OSMID", 2);
                                IFeatureCursor polygonFeatureCursor = osmPolygonFeatureClass.Search(osmIDQueryFilter, false);
                                comReleaser.ManageLifetime(polygonFeatureCursor);

                                IFeature foundPolygonFeature = polygonFeatureCursor.NextFeature();

                                if (foundPolygonFeature != null)
                                {
                                    IPointCollection pointCollection = foundPolygonFeature.Shape as IPointCollection;

                                    int firstPointID = pointCollection.get_Point(0).ID;

                                    if (nodeCountDictionary.ContainsKey(firstPointID))
                                    {
                                        nodeCountDictionary[firstPointID] = nodeCountDictionary[firstPointID] + 2;
                                    }
                                    else
                                    {
                                        nodeCountDictionary.Add(firstPointID, 2);
                                    }
                                }

                                osmIDQueryFilter.WhereClause = relationTable.WhereClauseByExtensionVersion(memberItem.@ref, "OSMID", 2);
                                ICursor relationCursor = relationTable.Search(osmIDQueryFilter, false);
                                comReleaser.ManageLifetime(relationCursor);

                                IRow foundRelation = relationCursor.NextRow();

                                if (foundRelation != null)
                                {
                                    // in order to be in the relation table is needs to be a either a super-relation or a mixed type entity (hence hybrid)
                                    testedGeometry = osmRelationGeometryType.osmHybridGeometry;
                                    break;
                                }
                            }
                        }
                    }
                }

                // check if there are any nodes counts other than 2, if there are then we cannot fully "walk" the geometry and it will be considered a line
                if (nodeCountDictionary.Values.Any(e => (e != 2)))
                {
                    testedGeometry = osmRelationGeometryType.osmPolyline;
                }
                else
                {
                    testedGeometry = osmRelationGeometryType.osmPolygon;
                }
            }

            catch
            {
                testedGeometry = osmRelationGeometryType.osmHybridGeometry;
            }

            return testedGeometry;
        }
        /// <summary>
        /// This function assembles a version of the logged relation action from the revision table in the OsmChange format http://wiki.openstreetmap.org/wiki/OsmChange
        /// </summary>
        /// <param name="relationTable"></param>
        /// <param name="action"></param>
        /// <param name="osmID"></param>
        /// <param name="changeSetID"></param>
        /// <param name="osmVersion"></param>
        /// <param name="nodeosmIDLookup"></param>
        /// <param name="wayosmIDLookup"></param>
        /// <param name="relationosmIDLookup"></param>
        /// <param name="extensionVersion"></param>
        /// <returns></returns>
        private ESRI.ArcGIS.OSM.OSMClassExtension.relation CreateRelationRepresentation(ITable relationTable, string action, long osmID, string changeSetID, int osmVersion, Dictionary<long, long> nodeosmIDLookup, Dictionary<long, long> wayosmIDLookup, Dictionary<long, long> relationosmIDLookup, int extensionVersion)
        {
            ESRI.ArcGIS.OSM.OSMClassExtension.relation relationRepresentation = new ESRI.ArcGIS.OSM.OSMClassExtension.relation();

            // let's find all the rows that have a different status than 200 - meaning success
            IQueryFilter queryFilter = new QueryFilterClass();
            queryFilter.WhereClause = relationTable.WhereClauseByExtensionVersion(osmID, "OSMID", extensionVersion);

            using (ComReleaser comReleaser = new ComReleaser())
            {
                ICursor searchCursor = relationTable.Search(queryFilter, false);
                comReleaser.ManageLifetime(searchCursor);

                IRow relationRow = searchCursor.NextRow();

                int osmTagsFieldIndex = relationTable.Fields.FindField("osmTags");
                int osmIDFieldIndex = relationTable.Fields.FindField("osmID");
                int osmUserFieldIndex = relationTable.Fields.FindField("osmuser");
                int osmUIDFieldIndex = relationTable.Fields.FindField("osmuid");
                int osmVisibleFieldIndex = relationTable.Fields.FindField("osmvisible");
                int osmVersionFieldIndex = relationTable.Fields.FindField("osmversion");
                int osmMembersFieldIndex = relationTable.Fields.FindField("osmMembers");

                if (relationRow != null)
                {
                    switch (action)
                    {
                        case "create":
                            // the newly created node needs to carry the changeset info, the coordinate and the tags
                            relationRepresentation.changeset = changeSetID;

                            ESRI.ArcGIS.OSM.OSMClassExtension.tag[] tags = null;
                            if (osmTagsFieldIndex > -1)
                            {
                                tags = _osmUtility.retrieveOSMTags(relationRow, osmTagsFieldIndex, ((IDataset)relationTable).Workspace);
                            }

                            List<tag> valueOnlyTags = new List<tag>();

                            for (int index = 0; index < tags.Length; index++)
                            {
                                if (!String.IsNullOrEmpty(tags[index].v))
                                {
                                    valueOnlyTags.Add(tags[index]);
                                }
                            }

                            if (osmIDFieldIndex > -1)
                            {
                                relationRepresentation.id = osmID.ToString();
                            }

                            ESRI.ArcGIS.OSM.OSMClassExtension.member[] members = null;
                            if (osmMembersFieldIndex > -1)
                            {
                                members = _osmUtility.retrieveMembers(relationRow, osmMembersFieldIndex);

                                // run the member ids through the lookup table
                                for (int memberIndex = 0; memberIndex < members.Length; memberIndex++)
                                {
                                    switch (members[memberIndex].type)
                                    {
                                        case ESRI.ArcGIS.OSM.OSMClassExtension.memberType.way:
                                            if (wayosmIDLookup.ContainsKey(Convert.ToInt64(members[memberIndex].@ref)))
                                            {
                                                members[memberIndex].@ref = Convert.ToString(wayosmIDLookup[Convert.ToInt64(members[memberIndex].@ref)]);
                                            }
                                            break;
                                        case ESRI.ArcGIS.OSM.OSMClassExtension.memberType.node:
                                            if (nodeosmIDLookup.ContainsKey(Convert.ToInt64(members[memberIndex].@ref)))
                                            {
                                                members[memberIndex].@ref = Convert.ToString(nodeosmIDLookup[Convert.ToInt64(members[memberIndex].@ref)]);
                                            }
                                            break;
                                        case ESRI.ArcGIS.OSM.OSMClassExtension.memberType.relation:
                                            if (relationosmIDLookup.ContainsKey(Convert.ToInt64(members[memberIndex].@ref)))
                                            {
                                                members[memberIndex].@ref = Convert.ToString(relationosmIDLookup[Convert.ToInt64(members[memberIndex].@ref)]);
                                            }
                                            break;
                                        default:
                                            break;
                                    }
                                }

                                _osmUtility.insertMembers(osmMembersFieldIndex, relationRow, members);
                            }

                            // add the member and the tags to the relation element
                            List<object> relationItems = new List<object>();
                            relationItems.AddRange(members);
                            relationItems.AddRange(valueOnlyTags.ToArray());

                            relationRepresentation.Items = relationItems.ToArray();

                            break;
                        case "modify":
                            // for an update the complete (full) relation needs to be returned
                            relationRepresentation.changeset = changeSetID;
                            if (osmIDFieldIndex > -1)
                            {
                                relationRepresentation.id = Convert.ToString(relationRow.get_Value(osmIDFieldIndex), new CultureInfo("en-US"));
                            }

                            if (osmUserFieldIndex > -1)
                            {
                                relationRepresentation.user = Convert.ToString(relationRow.get_Value(osmUserFieldIndex));
                            }

                            if (osmUIDFieldIndex > -1)
                            {
                                relationRepresentation.uid = Convert.ToString(relationRow.get_Value(osmUIDFieldIndex), new CultureInfo("en-US"));
                            }

                            if (osmVersionFieldIndex > -1)
                            {
                                relationRepresentation.version = Convert.ToString(relationRow.get_Value(osmVersionFieldIndex));
                            }

                            tags = null;
                            if (osmTagsFieldIndex > -1)
                            {
                                tags = _osmUtility.retrieveOSMTags((IRow)relationRow, osmTagsFieldIndex, ((IDataset)relationTable).Workspace);
                            }

                            valueOnlyTags = new List<tag>();

                            for (int index = 0; index < tags.Length; index++)
                            {
                                if (!String.IsNullOrEmpty(tags[index].v))
                                {
                                    valueOnlyTags.Add(tags[index]);
                                }
                            }

                            members = null;
                            if (osmMembersFieldIndex > -1)
                            {
                                members = _osmUtility.retrieveMembers(relationRow, osmMembersFieldIndex);

                                // run the member ids through the lookup table
                                for (int memberIndex = 0; memberIndex < members.Length; memberIndex++)
                                {
                                    switch (members[memberIndex].type)
                                    {
                                        case memberType.way:
                                            if (wayosmIDLookup.ContainsKey(Convert.ToInt64(members[memberIndex].@ref)))
                                            {
                                                members[memberIndex].@ref = Convert.ToString(wayosmIDLookup[Convert.ToInt64(members[memberIndex].@ref)]);
                                            }
                                            break;
                                        case memberType.node:
                                            if (nodeosmIDLookup.ContainsKey(Convert.ToInt64(members[memberIndex].@ref)))
                                            {
                                                members[memberIndex].@ref = Convert.ToString(nodeosmIDLookup[Convert.ToInt64(members[memberIndex].@ref)]);
                                            }
                                            break;
                                        case memberType.relation:
                                            if (relationosmIDLookup.ContainsKey(Convert.ToInt64(members[memberIndex].@ref)))
                                            {
                                                members[memberIndex].@ref = Convert.ToString(relationosmIDLookup[Convert.ToInt64(members[memberIndex].@ref)]);
                                            }
                                            break;
                                        default:
                                            break;
                                    }
                                }

                                _osmUtility.insertMembers(osmMembersFieldIndex, relationRow, members);
                            }

                            // add the member and the tags to the relation element
                            relationItems = new List<object>();
                            relationItems.AddRange(valueOnlyTags.ToArray());
                            relationItems.AddRange(members);

                            relationRepresentation.Items = relationItems.ToArray();

                            break;
                        case "delete":

                            relationRepresentation.changeset = changeSetID;
                            relationRepresentation.id = Convert.ToString(osmID);
                            relationRepresentation.version = Convert.ToString(osmVersion);

                            break;
                        default:
                            break;
                    }
                }
                else
                {
                    if (action.Equals("delete", StringComparison.InvariantCultureIgnoreCase))
                    {
                        relationRepresentation.changeset = changeSetID;
                        relationRepresentation.id = Convert.ToString(osmID);
                        relationRepresentation.version = Convert.ToString(osmVersion);
                    }
                }
            }

            return relationRepresentation;
        }
        //internal List<ESRI.ArcGIS.OSM.OSMClassExtension.tag> MergeTagsFromOuterPolygonToRelation(ESRI.ArcGIS.OSM.OSMClassExtension.relation currentRelation, IFeatureClass polygonFeatureClass)
        //{
        //    Dictionary<string, ESRI.ArcGIS.OSM.OSMClassExtension.tag> mergedTagList = new Dictionary<string, ESRI.ArcGIS.OSM.OSMClassExtension.tag>();
        //    IQueryFilter osmIDQueryFilter = new QueryFilterClass();
        //    try
        //    {
        //        int osmIDPolygonFieldIndex = polygonFeatureClass.FindField("OSMID");
        //        string sqlPolyOSMID = polygonFeatureClass.SqlIdentifier("OSMID");
        //        foreach (var relationItem in currentRelation.Items)
        //        {
        //            if (relationItem is ESRI.ArcGIS.OSM.OSMClassExtension.member)
        //            {
        //                ESRI.ArcGIS.OSM.OSMClassExtension.member currentRelationMember = relationItem as ESRI.ArcGIS.OSM.OSMClassExtension.member;
        //                if (currentRelationMember.role.ToLower().Equals("outer"))
        //                {
        //                    using (ComReleaser comReleaser = new ComReleaser())
        //                    {
        //                        osmIDQueryFilter.WhereClause = sqlPolyOSMID + " = " + currentRelationMember.@ref;
        //                        IFeatureCursor featureCursor = polygonFeatureClass.Search(osmIDQueryFilter, false);
        //                        comReleaser.ManageLifetime(featureCursor);
        //                        IFeature foundPolygonFeature = featureCursor.NextFeature();
        //                        if (foundPolygonFeature == null)
        //                            continue;
        //                        tag[] foundTags = OSMUtility.retrieveOSMTags(foundPolygonFeature, osmIDPolygonFieldIndex, ((IDataset)polygonFeatureClass).Workspace);
        //                        foreach (tag currentWayTag in foundTags)
        //                        {
        //                            // first one in wins
        //                            try
        //                            {
        //                                if (!mergedTagList.ContainsKey(currentWayTag.k))
        //                                {
        //                                    mergedTagList.Add(currentWayTag.k, currentWayTag);
        //                                }
        //                            }
        //                            catch { }
        //                        }
        //                    }
        //                }
        //            }
        //            else if (relationItem is tag)
        //            {
        //                tag relationTag = relationItem as tag;
        //                try
        //                {
        //                    if (!mergedTagList.ContainsKey(relationTag.k))
        //                    {
        //                        mergedTagList.Add(relationTag.k, relationTag);
        //                    }
        //                }
        //                catch { }
        //            }
        //        }
        //    }
        //    catch { }
        //    return mergedTagList.Values.ToList();
        //}
        internal osmRelationGeometryType determineOSMGeometryType(IFeatureClass lineFeatureClass, IFeatureClass polygonFeatureClass, ITable relationTable, string osmIDtoFind)
        {
            osmRelationGeometryType determinedGeometryType = osmRelationGeometryType.osmUnknownGeometry;

            try
            {
                IQueryFilter osmIDQueryFilter = new QueryFilterClass();
                osmIDQueryFilter.SubFields = "OSMID";

                using (ComReleaser comReleaser = new ComReleaser())
                {
                    if (lineFeatureClass != null)
                    {
                        osmIDQueryFilter.WhereClause = lineFeatureClass.WhereClauseByExtensionVersion(osmIDtoFind, "OSMID", 2);
                        IFeatureCursor lineFeatureCursor = lineFeatureClass.Search(osmIDQueryFilter, false);
                        comReleaser.ManageLifetime(lineFeatureCursor);

                        IFeature foundLineFeature = lineFeatureCursor.NextFeature();
                        if (foundLineFeature != null)
                        {
                            determinedGeometryType = osmRelationGeometryType.osmPolyline;
                            return determinedGeometryType;
                        }

                        osmIDQueryFilter.WhereClause = polygonFeatureClass.WhereClauseByExtensionVersion(osmIDtoFind, "OSMID", 2);
                        IFeatureCursor polygonFeatureCursor = polygonFeatureClass.Search(osmIDQueryFilter, false);
                        comReleaser.ManageLifetime(polygonFeatureCursor);

                        IFeature foundPolygonFeature = polygonFeatureCursor.NextFeature();
                        if (foundPolygonFeature != null)
                        {
                            determinedGeometryType = osmRelationGeometryType.osmPolygon;
                            return determinedGeometryType;
                        }

                        osmIDQueryFilter.WhereClause = relationTable.WhereClauseByExtensionVersion(osmIDtoFind, "OSMID", 2);
                        ICursor relationCursor = relationTable.Search(osmIDQueryFilter, false);
                        comReleaser.ManageLifetime(relationCursor);

                        IRow foundRelation = relationCursor.NextRow();
                        if (foundRelation != null)
                        {
                            // in order to be in the relation table is needs to be a either a super-relation or a mixed type entity (hence hybrid)
                            determinedGeometryType = osmRelationGeometryType.osmHybridGeometry;
                            return determinedGeometryType;
                        }
                    }
                }
            }
            catch
            {
            }

            return determinedGeometryType;
        }