Advertisement
chmodseven

Browse OSM Tags in CityGen3D Database files

Apr 3rd, 2022 (edited)
975
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 48.39 KB | None | 0 0
  1. #if UNITY_EDITOR
  2.  
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text;
  7. using CityGen3D;
  8. using UnityEditor;
  9. using UnityEngine;
  10.  
  11. /*
  12.     NOTE: When using Data on a CityGen3D object in the scene hierarchy that links back to a prefab,
  13.     for some reason, the types/tags lists in the various Data categories seem to get refreshed from the prefab when
  14.     you reload Unity, which overwrites any changes made in the hierarchy copy. This happens even if not using this tool
  15.     and just adding/deleting types or tags using the CityGen3D Data interface - either the prefab instance needs to be unpacked,
  16.     or you need to remember to clear the data and then apply overrides back to the prefab after making edits    
  17. */
  18.  
  19. namespace Tesseraction.CityGenExtensions
  20. {
  21.     public class BrowseOsmTagsWindow : EditorWindow
  22.     {
  23.         private const float FixedWindowWidth = 500f;
  24.         private const float DefaultWindowHeight = 800f;
  25.         private const float MinWindowHeight = 500f;
  26.         private const float MaxWindowHeight = 4000f;
  27.         private const string IsTagKnownColor = "<color=lime>";
  28.         private const string TagFilterColor = "<color=yellow><b>";
  29.         private const string TagKnownFilterColor = "<color=olive><b>";
  30.  
  31.         private Vector2 _overallScrollPos;
  32.         private Dictionary<Database, bool> _foldoutNodes = new ();
  33.         private Dictionary<Database, bool> _foldoutRelations = new ();
  34.         private Dictionary<Database, bool> _foldoutWays = new ();
  35.         private readonly Dictionary<string, Database> _databases = new ();
  36.         private Dictionary<Database, Dictionary<string, TagCounts>> _tagCountsNodes = new ();
  37.         private Dictionary<Database, Dictionary<string, TagCounts>> _tagCountsRelations = new ();
  38.         private Dictionary<Database, Dictionary<string, TagCounts>> _tagCountsWays = new ();
  39.         private readonly Dictionary<string, TagCounts> _tagDropdowns = new ();
  40.         private Dictionary<string, HashSet<string>> _knownTags = new ();
  41.         private Dictionary<string, HashSet<string>> _knownSurfaceTags = new ();
  42.         private Dictionary<string, HashSet<string>> _knownRoadTags = new ();
  43.         private Dictionary<string, HashSet<string>> _knownBuildingTags = new ();
  44.         private Dictionary<string, HashSet<string>> _knownEntityTags = new ();
  45.         private Dictionary<string, HashSet<string>> _knownFeatureTags = new ();
  46.         private Dictionary<string, HashSet<string>> _knownTreeTags = new ();
  47.         private bool _isInitialized;
  48.         private string _searchField = "";
  49.         private bool _searchFilterTag;
  50.         private bool _searchFilterValue;
  51.  
  52.         [MenuItem ("Tools/CityGen3D/Browse OSM Tags", priority = 12)]
  53.         private static void InitializeFromMenuOption ()
  54.         {
  55.             BrowseOsmTagsWindow window = (BrowseOsmTagsWindow) GetWindow (typeof (BrowseOsmTagsWindow));
  56.             window.Initialize (FixedWindowWidth, DefaultWindowHeight);
  57.         }
  58.  
  59.         public void Reset ()
  60.         {
  61.             BrowseOsmTagsWindow window = (BrowseOsmTagsWindow) GetWindow (typeof (BrowseOsmTagsWindow));
  62.             window.titleContent = new GUIContent ("Browse OSM Tags");
  63.             Initialize (window.position.width, window.position.height);
  64.         }
  65.  
  66.         private void Initialize (float windowWidth, float windowHeight)
  67.         {
  68.             Map map = Map.Instance;
  69.             {
  70.                 if (map == null)
  71.                 {
  72.                     Debug.LogError ("No CityGen3D Map object in scene");
  73.                     return;
  74.                 }
  75.             }
  76.  
  77.             _searchField = string.Empty;
  78.             _searchFilterTag = false;
  79.             _searchFilterValue = false;
  80.             _foldoutNodes.Clear ();
  81.             _foldoutRelations.Clear ();
  82.             _foldoutWays.Clear ();
  83.             _databases.Clear ();
  84.             _tagCountsNodes.Clear ();
  85.             _tagCountsRelations.Clear ();
  86.             _tagCountsWays.Clear ();
  87.             _tagDropdowns.Clear ();
  88.             _knownTags.Clear ();
  89.             _knownSurfaceTags.Clear ();
  90.             _knownRoadTags.Clear ();
  91.             _knownBuildingTags.Clear ();
  92.             _knownEntityTags.Clear ();
  93.             _knownFeatureTags.Clear ();
  94.             _knownTreeTags.Clear ();
  95.  
  96.             if (Map.Instance.mapSurfaces != null)
  97.             {
  98.                 foreach (ObjectTag tags in Map.Instance.mapSurfaces.surfaceTags)
  99.                 {
  100.                     AddKnownTag (ref _knownTags, tags);
  101.                     AddKnownTag (ref _knownSurfaceTags, tags);
  102.                 }
  103.             }
  104.             if (Map.Instance.mapRoads != null)
  105.             {
  106.                 foreach (ObjectTag tags in Map.Instance.mapRoads.roadTags)
  107.                 {
  108.                     AddKnownTag (ref _knownTags, tags);
  109.                     AddKnownTag (ref _knownRoadTags, tags);
  110.                 }
  111.             }
  112.             if (Map.Instance.mapBuildings != null)
  113.             {
  114.                 foreach (ObjectTag tags in Map.Instance.mapBuildings.buildingTags)
  115.                 {
  116.                     AddKnownTag (ref _knownTags, tags);
  117.                     AddKnownTag (ref _knownBuildingTags, tags);
  118.                 }
  119.             }
  120.             if (Map.Instance.mapEntities != null)
  121.             {
  122.                 foreach (ObjectTag tags in Map.Instance.mapEntities.entityTags)
  123.                 {
  124.                     AddKnownTag (ref _knownTags, tags);
  125.                     AddKnownTag (ref _knownEntityTags, tags);
  126.                 }
  127.             }
  128.             if (Map.Instance.mapFeatures != null)
  129.             {
  130.                 foreach (ObjectTag tags in Map.Instance.mapFeatures.featureTags)
  131.                 {
  132.                     AddKnownTag (ref _knownTags, tags);
  133.                     AddKnownTag (ref _knownFeatureTags, tags);
  134.                 }
  135.             }
  136.             if (Map.Instance.mapTrees != null)
  137.             {
  138.                 foreach (ObjectTag tags in Map.Instance.mapTrees.treeTags)
  139.                 {
  140.                     AddKnownTag (ref _knownTags, tags);
  141.                     AddKnownTag (ref _knownTreeTags, tags);
  142.                 }
  143.             }
  144.  
  145.             SetInitialWindowRectSize (windowWidth, windowHeight);
  146.             SetFlexibleWindowRectSize ();
  147.  
  148.             string databasePath = Path.Combine (Application.dataPath, "Database");
  149.             string [] databaseFiles = Directory.GetFiles (databasePath);
  150.             if (databaseFiles.Length == 0)
  151.             {
  152.                 return;
  153.             }
  154.  
  155.             _foldoutNodes = new Dictionary<Database, bool> ();
  156.             _foldoutRelations = new Dictionary<Database, bool> ();
  157.             _foldoutWays = new Dictionary<Database, bool> ();
  158.  
  159.             string filePath = string.Empty;
  160.             foreach (string databaseFile in databaseFiles)
  161.             {
  162.                 if (databaseFile.EndsWith (".meta"))
  163.                 {
  164.                     continue;
  165.                 }
  166.  
  167.                 string fileName = Path.GetFileName (databaseFile);
  168.                 filePath = Path.Combine ("Assets", "Database", fileName);
  169.                 _databases.Add (filePath, null);
  170.             }
  171.  
  172.             if (_databases.Count == 1)
  173.             {
  174.                 LoadDatabase (filePath);
  175.             }
  176.  
  177.             _isInitialized = true;
  178.         }
  179.  
  180.         private static void AddKnownTag (ref Dictionary<string, HashSet<string>> tagSet, ObjectTag tags)
  181.         {
  182.             if (!tagSet.ContainsKey (tags.tag.key))
  183.             {
  184.                 tagSet.Add (tags.tag.key, new HashSet<string> ());
  185.             }
  186.  
  187.             if (!tagSet [tags.tag.key].Contains (tags.tag.value))
  188.             {
  189.                 tagSet [tags.tag.key].Add (tags.tag.value);
  190.             }
  191.         }
  192.  
  193.         private void LoadDatabase (string filePath)
  194.         {
  195.             Database db = AssetDatabase.LoadAssetAtPath<Database> (filePath);
  196.             if (db == null)
  197.             {
  198.                 throw new MissingComponentException ();
  199.             }
  200.  
  201.             _foldoutNodes.Add (db, false);
  202.             _foldoutRelations.Add (db, false);
  203.             _foldoutWays.Add (db, false);
  204.  
  205.             List<List<OSM_Tag>> allNodeTags = db.mapNodes.Select (x => x.tags).ToList ();
  206.             LoadTagsAndCounts (ref _tagCountsNodes, allNodeTags, db);
  207.  
  208.             List<List<OSM_Tag>> allRelationTags = db.mapRelations.Select (x => x.tags).ToList ();
  209.             LoadTagsAndCounts (ref _tagCountsRelations, allRelationTags, db);
  210.  
  211.             List<List<OSM_Tag>> allWayTags = db.mapWays.Select (x => x.tags).ToList ();
  212.             LoadTagsAndCounts (ref _tagCountsWays, allWayTags, db);
  213.  
  214.             _databases [filePath] = db;
  215.         }
  216.  
  217.         private void LoadTagsAndCounts (
  218.             ref Dictionary<Database, Dictionary<string, TagCounts>> tagCountSet,
  219.             List<List<OSM_Tag>> allTags,
  220.             Database db)
  221.         {
  222.             HashSet<string> uniqueTags = new ();
  223.             Dictionary<string, TagCounts> tagCounts = new ();
  224.             foreach (List<OSM_Tag> tagSet in allTags)
  225.             {
  226.                 uniqueTags.UnionWith (tagSet.Select (x => x.key));
  227.             }
  228.  
  229.             List<string> sortedTags = new (uniqueTags);
  230.             sortedTags.Sort ();
  231.             foreach (string tag in sortedTags)
  232.             {
  233.                 tagCounts.Add (tag, new TagCounts (0, new Dictionary<string, int> ()));
  234.             }
  235.  
  236.             foreach (List<OSM_Tag> tagSet in allTags)
  237.             {
  238.                 foreach (OSM_Tag osmTag in tagSet)
  239.                 {
  240.                     tagCounts [osmTag.key].count++;
  241.                     if (!tagCounts [osmTag.key].valueCounts.ContainsKey (osmTag.value))
  242.                     {
  243.                         tagCounts [osmTag.key].valueCounts.Add (osmTag.value, 0);
  244.                     }
  245.                     tagCounts [osmTag.key].valueCounts [osmTag.value]++;
  246.                 }
  247.             }
  248.  
  249.             tagCountSet.Add (db, tagCounts);
  250.             foreach (KeyValuePair<string, TagCounts> pair in tagCounts)
  251.             {
  252.                 if (!_tagDropdowns.ContainsKey (pair.Key))
  253.                 {
  254.                     _tagDropdowns.Add (pair.Key, new TagCounts (pair.Value.count, new Dictionary<string, int> ()));
  255.                 }
  256.                 foreach (KeyValuePair<string, int> valuePair in pair.Value.valueCounts)
  257.                 {
  258.                     if (!_tagDropdowns [pair.Key].valueCounts.ContainsKey (valuePair.Key))
  259.                     {
  260.                         _tagDropdowns [pair.Key].valueCounts.Add (valuePair.Key, 0);
  261.                     }
  262.                 }
  263.             }
  264.         }
  265.  
  266.         private static BrowseOsmTagsWindow GetWindowReference ()
  267.         {
  268.             // Get the window without changing the focus away from the current window
  269.             return (BrowseOsmTagsWindow) GetWindow (typeof (BrowseOsmTagsWindow), false, "Find OSM Tags", false);
  270.         }
  271.  
  272.         private static void SetInitialWindowRectSize (float windowWidth, float windowHeight)
  273.         {
  274.             BrowseOsmTagsWindow window = GetWindowReference ();
  275.  
  276.             window.minSize = new Vector2 (windowWidth, windowHeight);
  277.             window.maxSize = new Vector2 (windowWidth, windowHeight);
  278.         }
  279.  
  280.         private static void SetFlexibleWindowRectSize ()
  281.         {
  282.             BrowseOsmTagsWindow window = GetWindowReference ();
  283.  
  284.             window.minSize = new Vector2 (FixedWindowWidth, MinWindowHeight);
  285.             window.maxSize = new Vector2 (FixedWindowWidth, MaxWindowHeight);
  286.         }
  287.  
  288.         private void OnGUI ()
  289.         {
  290.             BrowseOsmTagsWindow window = GetWindowReference ();
  291.             float windowWidth = window.position.width;
  292.             float windowHeight = window.position.height;
  293.  
  294.             GUIStyle labelStyle = new (EditorStyles.label) { richText = true };
  295.             GUILayoutOption loadButtonWidth = GUILayout.Width (90f);
  296.             GUILayoutOption exportButtonWidth = GUILayout.Width (90f);
  297.             GUILayoutOption resetButtonWidth = GUILayout.Width (90f);
  298.             GUILayoutOption searchFieldWidth = GUILayout.Width (windowWidth * 0.8f);
  299.             GUILayoutOption searchToggleWidth = GUILayout.Width (windowWidth * 0.025f);
  300.             GUILayoutOption searchIconWidth = GUILayout.Width (windowWidth * 0.05f);
  301.             GUILayoutOption headerWidth = GUILayout.Width (windowWidth * 0.75f);
  302.             GUILayoutOption databaseWidth = GUILayout.Width (windowWidth * 0.75f);
  303.             const float iconSize = 16f;
  304.  
  305.             if (!_isInitialized)
  306.             {
  307.                 EditorGUILayout.BeginHorizontal ();
  308.                 GUILayout.Label ("<color=red>Editor window not yet initialized</color>", labelStyle, headerWidth);
  309.                 if (GUILayout.Button ("Reset", resetButtonWidth))
  310.                 {
  311.                     Reset ();
  312.                 }
  313.                 EditorGUILayout.EndHorizontal ();
  314.                 return;
  315.             }
  316.  
  317.             // Assumes at least nodes always present in each database
  318.             if (_databases.Keys.Count == 0)
  319.             {
  320.                 string databasePath = Path.Combine (Application.dataPath, "Database");
  321.                 string [] databaseFiles = Directory.GetFiles (databasePath);
  322.                 if (databaseFiles.Length == 0)
  323.                 {
  324.                     EditorGUILayout.BeginHorizontal ();
  325.                     GUILayout.Label ("<color=red>No OSM databases found</color>", labelStyle, headerWidth);
  326.                     if (GUILayout.Button ("Reset", resetButtonWidth))
  327.                     {
  328.                         Reset ();
  329.                     }
  330.                     EditorGUILayout.EndHorizontal ();
  331.                     return;
  332.                 }
  333.  
  334.                 Reset ();
  335.             }
  336.  
  337.             _overallScrollPos = EditorGUILayout.BeginScrollView (_overallScrollPos,
  338.                 GUILayout.Width (windowWidth), GUILayout.Height (windowHeight));
  339.  
  340.             EditorGUILayout.BeginHorizontal ();
  341.             GUILayout.Label ("<color=green>" + _databases.Keys.Count + " OSM database" +
  342.                              (_databases.Keys.Count == 1 ? " " : "s ") + "found</color>", labelStyle, headerWidth);
  343.             if (GUILayout.Button ("Reset", resetButtonWidth))
  344.             {
  345.                 Reset ();
  346.             }
  347.             EditorGUILayout.EndHorizontal ();
  348.             GUILayout.Space (10);
  349.  
  350.             Vector2 oldIconSize = EditorGUIUtility.GetIconSize ();
  351.             EditorGUIUtility.SetIconSize (new Vector2 (iconSize, iconSize));
  352.             EditorGUILayout.BeginHorizontal ();
  353.             _searchField = GUILayout.TextField (_searchField, searchFieldWidth);
  354.             GUIContent icon = EditorGUIUtility.IconContent ("d_Search Icon");
  355.             EditorGUILayout.LabelField (icon, searchIconWidth);
  356.             _searchFilterTag = GUILayout.Toggle (_searchFilterTag, new GUIContent ("", "Filter tags"), searchToggleWidth);
  357.             _searchFilterValue = GUILayout.Toggle (_searchFilterValue, new GUIContent ("", "Filter values"), searchToggleWidth);
  358.             EditorGUILayout.EndHorizontal ();
  359.             EditorGUIUtility.SetIconSize (oldIconSize);
  360.             bool hasSearch = !string.IsNullOrEmpty (_searchField);
  361.  
  362.             foreach (string filePath in _databases.Keys.ToArray ())
  363.             {
  364.                 Database database = _databases [filePath];
  365.                 GUILayout.Space (10);
  366.  
  367.                 if (database == null)
  368.                 {
  369.                     EditorGUILayout.BeginHorizontal ();
  370.                     GUILayout.Label ("<color=lightblue>Database at path " +
  371.                                      Path.GetFileNameWithoutExtension (filePath) + "</color>", labelStyle, databaseWidth);
  372.                     if (GUILayout.Button ("Load", loadButtonWidth))
  373.                     {
  374.                         EditorUtility.ClearProgressBar ();
  375.                         EditorUtility.DisplayProgressBar ("Loading data", "Loading OSM data from " +
  376.                                                                           Path.GetFileNameWithoutExtension (filePath), 0.5f);
  377.                         LoadDatabase (filePath);
  378.                         EditorUtility.ClearProgressBar ();
  379.                     }
  380.                     EditorGUILayout.EndHorizontal ();
  381.                     continue;
  382.                 }
  383.  
  384.                 EditorGUILayout.BeginHorizontal ();
  385.                 GUILayout.Label ("<color=cyan>Database " + database.name + "</color>", labelStyle, databaseWidth);
  386.                 if (GUILayout.Button ("Export", exportButtonWidth))
  387.                 {
  388.                     ExportCsvDataToClipboard (database);
  389.                 }
  390.                 EditorGUILayout.EndHorizontal ();
  391.                 string searchTagReplacement = TagFilterColor + _searchField + "</b></color>";
  392.                 string searchKnownTagReplacement = TagKnownFilterColor + _searchField + "</b></color>";
  393.  
  394.                 GUILayout.Space (6);
  395.                 ProcessTags (
  396.                     database,
  397.                     ref _tagCountsNodes,
  398.                     ref _foldoutNodes,
  399.                     hasSearch,
  400.                     searchTagReplacement,
  401.                     searchKnownTagReplacement,
  402.                     "Nodes",
  403.                     windowWidth);
  404.  
  405.                 GUILayout.Space (6);
  406.                 ProcessTags (
  407.                     database,
  408.                     ref _tagCountsRelations,
  409.                     ref _foldoutRelations,
  410.                     hasSearch,
  411.                     searchTagReplacement,
  412.                     searchKnownTagReplacement,
  413.                     "Relations",
  414.                     windowWidth);
  415.  
  416.                 GUILayout.Space (6);
  417.                 ProcessTags (
  418.                     database,
  419.                     ref _tagCountsWays,
  420.                     ref _foldoutWays,
  421.                     hasSearch,
  422.                     searchTagReplacement,
  423.                     searchKnownTagReplacement,
  424.                     "Ways",
  425.                     windowWidth);
  426.             }
  427.  
  428.             EditorGUILayout.EndScrollView ();
  429.         }
  430.  
  431.         private void ProcessTags (
  432.             Database database,
  433.             ref Dictionary<Database, Dictionary<string, TagCounts>> tagCountSet,
  434.             ref Dictionary<Database, bool> foldoutSet,
  435.             bool hasSearch,
  436.             string searchTagReplacement,
  437.             string searchKnownTagReplacement,
  438.             string setType,
  439.             float windowWidth)
  440.         {
  441.             GUIStyle labelStyle = new (EditorStyles.label) { richText = true };
  442.             GUIStyle rightJustifiedLabelStyle = new (EditorStyles.label) { richText = true, alignment = TextAnchor.MiddleRight };
  443.             GUILayoutOption addButtonWidth = GUILayout.Width (40f);
  444.             GUILayoutOption deleteButtonWidth = GUILayout.Width (40f);
  445.             GUILayoutOption tagWidth = GUILayout.Width (windowWidth * 0.4f);
  446.             GUILayoutOption countWidth = GUILayout.Width (windowWidth * 0.15f);
  447.             GUILayoutOption indentedToggleWidth = GUILayout.Width (windowWidth * 0.14f);
  448.             GUILayoutOption indentedWidth = GUILayout.Width (windowWidth * 0.05f);
  449.  
  450.             int countTags = tagCountSet [database].Keys.Count;
  451.             int countValues = 0;
  452.             foreach (TagCounts tagCounts in tagCountSet [database].Values)
  453.             {
  454.                 countValues += tagCounts.count;
  455.             }
  456.             string setTitle = setType + " -- " + countTags.ToString ("N0") + " keys, " + countValues.ToString ("N0") + " values";
  457.             foldoutSet [database] = EditorGUILayout.Foldout (foldoutSet [database], setTitle);
  458.             if (!foldoutSet [database])
  459.             {
  460.                 return;
  461.             }
  462.  
  463.             EditorGUILayout.BeginVertical ();
  464.             foreach (string tag in tagCountSet [database].Keys)
  465.             {
  466.                 Dictionary<string, int> sortedValues = tagCountSet [database] [tag].valueCounts;
  467.                 if (hasSearch && !tag.Contains (_searchField))
  468.                 {
  469.                     if (_searchFilterTag && !sortedValues.Any (x => x.Key.Contains (_searchField)))
  470.                     {
  471.                         continue;
  472.                     }
  473.                 }
  474.  
  475.                 string tagDisplay = tag;
  476.                 string knownTagDisplay = tag;
  477.                 if (hasSearch && tag.Contains (_searchField))
  478.                 {
  479.                     tagDisplay = tagDisplay.Replace (_searchField, searchTagReplacement);
  480.                     knownTagDisplay = knownTagDisplay.Replace (_searchField, searchKnownTagReplacement);
  481.                 }
  482.  
  483.                 EditorGUILayout.BeginHorizontal ();
  484.                 bool isTagKnown = _knownTags.ContainsKey (tag);
  485.                 if (isTagKnown)
  486.                 {
  487.                     GUILayout.Label (IsTagKnownColor + knownTagDisplay + "</color>", labelStyle, tagWidth);
  488.                 }
  489.                 else
  490.                 {
  491.                     GUILayout.Label ("<color=white>" + tagDisplay + "</color>", labelStyle, tagWidth);
  492.                 }
  493.                 GUILayout.Label (tagCountSet [database] [tag].count.ToString ("N0"), rightJustifiedLabelStyle, countWidth);
  494.                 GUILayout.Label ("", labelStyle, indentedToggleWidth);
  495.                 tagCountSet [database] [tag].showToggle = GUILayout.Toggle (
  496.                     tagCountSet [database] [tag].showToggle, " " + sortedValues.Count.ToString ("N0"), indentedToggleWidth);
  497.                 if (tagCountSet [database] [tag].showToggle)
  498.                 {
  499.                     EditorGUILayout.EndHorizontal ();
  500.                     EditorGUILayout.BeginVertical ();
  501.                     foreach (string sortedValue in sortedValues.Keys)
  502.                     {
  503.                         if (hasSearch && !sortedValue.Contains (_searchField))
  504.                         {
  505.                             if (_searchFilterValue)
  506.                             {
  507.                                 continue;
  508.                             }
  509.                         }
  510.  
  511.                         string sortedDisplay = sortedValue;
  512.                         string sortedKnownDisplay = sortedValue;
  513.                         if (hasSearch && sortedValue.Contains (_searchField))
  514.                         {
  515.                             sortedDisplay = sortedDisplay.Replace (_searchField, searchTagReplacement);
  516.                             sortedKnownDisplay = sortedKnownDisplay.Replace (_searchField, searchKnownTagReplacement);
  517.                         }
  518.  
  519.                         EditorGUILayout.BeginHorizontal ();
  520.                         GUILayout.Label ("", labelStyle, indentedWidth);
  521.  
  522.                         if (!isTagKnown)
  523.                         {
  524.                             GUILayout.Label ("<color=white>" + sortedDisplay + "</color>", labelStyle, tagWidth);
  525.                         }
  526.                         else
  527.                         {
  528.                             bool isKnown = _knownTags.ContainsKey (tag) && _knownTags [tag].Contains (sortedValue);
  529.                             if (isKnown)
  530.                             {
  531.                                 GUILayout.Label (IsTagKnownColor + sortedKnownDisplay + "</color>", labelStyle, tagWidth);
  532.                             }
  533.                             else
  534.                             {
  535.                                 GUILayout.Label ("<color=white>" + sortedDisplay + "</color>", labelStyle, tagWidth);
  536.                             }
  537.                         }
  538.  
  539.                         int count = tagCountSet [database] [tag].valueCounts [sortedValue];
  540.                         GUILayout.Label (count.ToString ("N0"), rightJustifiedLabelStyle, countWidth);
  541.  
  542.                         string alreadyTagged = string.Empty;
  543.                         ObjectTag selected = null;
  544.                         string [] options =
  545.                         {
  546.                             "None", "Surfaces", "Roads", "Buildings", "Entities", "Features", "Trees"
  547.                         };
  548.  
  549.                         if (_knownSurfaceTags.ContainsKey (tag) && _knownSurfaceTags [tag].Contains (sortedValue))
  550.                         {
  551.                             alreadyTagged = "Surfaces";
  552.                             options = Map.Instance.mapSurfaces.surfaces.Select (x => x.name).ToArray ();
  553.                             selected = Map.Instance.mapSurfaces.surfaceTags
  554.                                 .FirstOrDefault (x => x.tag.key == tag && x.tag.value == sortedValue);
  555.                         }
  556.                         if (_knownRoadTags.ContainsKey (tag) && _knownRoadTags [tag].Contains (sortedValue))
  557.                         {
  558.                             alreadyTagged = "Roads";
  559.                             options = Map.Instance.mapRoads.roads.Select (x => x.name).ToArray ();
  560.                             selected = Map.Instance.mapRoads.roadTags
  561.                                 .FirstOrDefault (x => x.tag.key == tag && x.tag.value == sortedValue);
  562.                         }
  563.                         if (_knownBuildingTags.ContainsKey (tag) && _knownBuildingTags [tag].Contains (sortedValue))
  564.                         {
  565.                             alreadyTagged = "Buildings";
  566.                             options = Map.Instance.mapBuildings.buildings.Select (x => x.name).ToArray ();
  567.                             selected = Map.Instance.mapBuildings.buildingTags
  568.                                 .FirstOrDefault (x => x.tag.key == tag && x.tag.value == sortedValue);
  569.                         }
  570.                         if (_knownEntityTags.ContainsKey (tag) && _knownEntityTags [tag].Contains (sortedValue))
  571.                         {
  572.                             alreadyTagged = "Entities";
  573.                             options = Map.Instance.mapEntities.entities.Select (x => x.name).ToArray ();
  574.                             selected = Map.Instance.mapEntities.entityTags
  575.                                 .FirstOrDefault (x => x.tag.key == tag && x.tag.value == sortedValue);
  576.                         }
  577.                         if (_knownFeatureTags.ContainsKey (tag) && _knownFeatureTags [tag].Contains (sortedValue))
  578.                         {
  579.                             alreadyTagged = "Features";
  580.                             options = Map.Instance.mapFeatures.features.Select (x => x.name).ToArray ();
  581.                             selected = Map.Instance.mapFeatures.featureTags
  582.                                 .FirstOrDefault (x => x.tag.key == tag && x.tag.value == sortedValue);
  583.                         }
  584.                         if (_knownTreeTags.ContainsKey (tag) && _knownTreeTags [tag].Contains (sortedValue))
  585.                         {
  586.                             alreadyTagged = "Trees";
  587.                             options = Map.Instance.mapTrees.trees.Select (x => x.name).ToArray ();
  588.                             selected = Map.Instance.mapTrees.treeTags
  589.                                 .FirstOrDefault (x => x.tag.key == tag && x.tag.value == sortedValue);
  590.                         }
  591.  
  592.                         if (!string.IsNullOrEmpty (alreadyTagged))
  593.                         {
  594.                             if (selected == null)
  595.                             {
  596.                                 ClearKnownTags (tag, sortedValue, alreadyTagged);
  597.                             }
  598.                             else
  599.                             {
  600.                                 _tagDropdowns [tag].valueCounts [sortedValue] = selected.index;
  601.                                 _tagDropdowns [tag].valueCounts [sortedValue] =
  602.                                     EditorGUILayout.Popup (_tagDropdowns [tag].valueCounts [sortedValue], options);
  603.                                 if (_tagDropdowns [tag].valueCounts [sortedValue] != selected.index)
  604.                                 {
  605.                                     switch (alreadyTagged)
  606.                                     {
  607.                                         case "Surfaces":
  608.                                             Undo.RecordObject (Map.Instance.mapSurfaces, "Change surface dropdown type");
  609.                                             selected.index = _tagDropdowns [tag].valueCounts [sortedValue];
  610.                                             EditorUtility.SetDirty (Map.Instance.mapSurfaces);
  611.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapSurfaces);
  612.                                             break;
  613.                                         case "Roads":
  614.                                             Undo.RecordObject (Map.Instance.mapRoads, "Change road dropdown type");
  615.                                             selected.index = _tagDropdowns [tag].valueCounts [sortedValue];
  616.                                             EditorUtility.SetDirty (Map.Instance.mapRoads);
  617.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapRoads);
  618.                                             break;
  619.                                         case "Buildings":
  620.                                             Undo.RecordObject (Map.Instance.mapBuildings, "Change building dropdown type");
  621.                                             selected.index = _tagDropdowns [tag].valueCounts [sortedValue];
  622.                                             EditorUtility.SetDirty (Map.Instance.mapBuildings);
  623.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapBuildings);
  624.                                             break;
  625.                                         case "Entities":
  626.                                             Undo.RecordObject (Map.Instance.mapEntities, "Change entity dropdown type");
  627.                                             selected.index = _tagDropdowns [tag].valueCounts [sortedValue];
  628.                                             EditorUtility.SetDirty (Map.Instance.mapEntities);
  629.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapEntities);
  630.                                             break;
  631.                                         case "Features":
  632.                                             Undo.RecordObject (Map.Instance.mapFeatures, "Change feature dropdown type");
  633.                                             selected.index = _tagDropdowns [tag].valueCounts [sortedValue];
  634.                                             EditorUtility.SetDirty (Map.Instance.mapFeatures);
  635.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapFeatures);
  636.                                             break;
  637.                                         case "Trees":
  638.                                             Undo.RecordObject (Map.Instance.mapTrees, "Change tree dropdown type");
  639.                                             selected.index = _tagDropdowns [tag].valueCounts [sortedValue];
  640.                                             EditorUtility.SetDirty (Map.Instance.mapTrees);
  641.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapTrees);
  642.                                             break;
  643.                                     }
  644.                                 }
  645.                                 if (GUILayout.Button ("Del", deleteButtonWidth))
  646.                                 {
  647.                                     ClearKnownTags (tag, sortedValue, alreadyTagged);
  648.                                     switch (alreadyTagged)
  649.                                     {
  650.                                         case "Surfaces":
  651.                                             Undo.RecordObject (Map.Instance.mapSurfaces, "Delete surface entry");
  652.                                             Map.Instance.mapSurfaces.surfaceTags.Remove (selected);
  653.                                             EditorUtility.SetDirty (Map.Instance.mapSurfaces);
  654.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapSurfaces);
  655.                                             break;
  656.                                         case "Roads":
  657.                                             Undo.RecordObject (Map.Instance.mapRoads, "Delete road entry");
  658.                                             Map.Instance.mapRoads.roadTags.Remove (selected);
  659.                                             EditorUtility.SetDirty (Map.Instance.mapRoads);
  660.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapRoads);
  661.                                             break;
  662.                                         case "Buildings":
  663.                                             Undo.RecordObject (Map.Instance.mapBuildings, "Delete building entry");
  664.                                             Map.Instance.mapBuildings.buildingTags.Remove (selected);
  665.                                             EditorUtility.SetDirty (Map.Instance.mapBuildings);
  666.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapBuildings);
  667.                                             break;
  668.                                         case "Entities":
  669.                                             Undo.RecordObject (Map.Instance.mapEntities, "Delete entity entry");
  670.                                             Map.Instance.mapEntities.entityTags.Remove (selected);
  671.                                             EditorUtility.SetDirty (Map.Instance.mapEntities);
  672.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapEntities);
  673.                                             break;
  674.                                         case "Features":
  675.                                             Undo.RecordObject (Map.Instance.mapFeatures, "Delete feature entry");
  676.                                             Map.Instance.mapFeatures.featureTags.Remove (selected);
  677.                                             EditorUtility.SetDirty (Map.Instance.mapFeatures);
  678.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapFeatures);
  679.                                             break;
  680.                                         case "Trees":
  681.                                             Undo.RecordObject (Map.Instance.mapTrees, "Delete tree entry");
  682.                                             Map.Instance.mapTrees.treeTags.Remove (selected);
  683.                                             EditorUtility.SetDirty (Map.Instance.mapTrees);
  684.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapTrees);
  685.                                             break;
  686.                                     }
  687.                                 }
  688.                             }
  689.                         }
  690.                         else
  691.                         {
  692.                             // Note: if you delete the tag from the Data window directly instead of using the delete button here,
  693.                             // it can give a control position error - this is because the component has changed in between Layout
  694.                             // and Repaint events in the GUI and caused a mismatch.  This apparently happens due to the GUI
  695.                             // running multiple times a frame and as such is not aligned with editor input very well
  696.                             // It's not a fatal problem, as this code will recover as well as it cvan, but the error is hard to hide
  697.                             _tagDropdowns [tag].valueCounts [sortedValue] =
  698.                                 EditorGUILayout.Popup (_tagDropdowns [tag].valueCounts [sortedValue], options);
  699.                             if (_tagDropdowns [tag].valueCounts [sortedValue] != 0)
  700.                             {
  701.                                 if (GUILayout.Button ("Add", addButtonWidth))
  702.                                 {
  703.                                     switch (_tagDropdowns [tag].valueCounts [sortedValue])
  704.                                     {
  705.                                         case 0:
  706.                                             break;
  707.                                         case 1:
  708.                                             Undo.RecordObject (Map.Instance.mapSurfaces, "Add surface entry");
  709.                                             int lastSurfaceID = Map.Instance.mapSurfaces.surfaces.Max (x => x.unique_id);
  710.                                             ObjectTag surfaceTag = new ()
  711.                                             {
  712.                                                 index = lastSurfaceID,
  713.                                                 tag = new OSM_Tag (tag, sortedValue)
  714.                                             };
  715.                                             AddKnownTag (ref _knownTags, surfaceTag);
  716.                                             AddKnownTag (ref _knownSurfaceTags, surfaceTag);
  717.                                             Map.Instance.mapSurfaces.surfaceTags.Add (surfaceTag);
  718.                                             EditorUtility.SetDirty (Map.Instance.mapSurfaces);
  719.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapSurfaces);
  720.                                             break;
  721.                                         case 2:
  722.                                             Undo.RecordObject (Map.Instance.mapRoads, "Add road entry");
  723.                                             int lastRoadID = Map.Instance.mapRoads.roads.Max (x => x.unique_id);
  724.                                             ObjectTag roadTag = new ()
  725.                                             {
  726.                                                 index = lastRoadID,
  727.                                                 tag = new OSM_Tag (tag, sortedValue)
  728.                                             };
  729.                                             AddKnownTag (ref _knownTags, roadTag);
  730.                                             AddKnownTag (ref _knownRoadTags, roadTag);
  731.                                             Map.Instance.mapRoads.roadTags.Add (roadTag);
  732.                                             EditorUtility.SetDirty (Map.Instance.mapRoads);
  733.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapRoads);
  734.                                             break;
  735.                                         case 3:
  736.                                             Undo.RecordObject (Map.Instance.mapBuildings, "Add building entry");
  737.                                             int lastBuildingID = Map.Instance.mapBuildings.buildings.Max (x => x.unique_id);
  738.                                             ObjectTag buildingTag = new ()
  739.                                             {
  740.                                                 index = lastBuildingID,
  741.                                                 tag = new OSM_Tag (tag, sortedValue)
  742.                                             };
  743.                                             AddKnownTag (ref _knownTags, buildingTag);
  744.                                             AddKnownTag (ref _knownBuildingTags, buildingTag);
  745.                                             Map.Instance.mapBuildings.buildingTags.Add (buildingTag);
  746.                                             EditorUtility.SetDirty (Map.Instance.mapBuildings);
  747.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapBuildings);
  748.                                             break;
  749.                                         case 4:
  750.                                             Undo.RecordObject (Map.Instance.mapEntities, "Add entity entry");
  751.                                             int lastEntityID = Map.Instance.mapEntities.entities.Max (x => x.unique_id);
  752.                                             ObjectTag entityTag = new ()
  753.                                             {
  754.                                                 index = lastEntityID,
  755.                                                 tag = new OSM_Tag (tag, sortedValue)
  756.                                             };
  757.                                             AddKnownTag (ref _knownTags, entityTag);
  758.                                             AddKnownTag (ref _knownEntityTags, entityTag);
  759.                                             Map.Instance.mapEntities.entityTags.Add (entityTag);
  760.                                             EditorUtility.SetDirty (Map.Instance.mapEntities);
  761.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapEntities);
  762.                                             break;
  763.                                         case 5:
  764.                                             Undo.RecordObject (Map.Instance.mapFeatures, "Add feature entry");
  765.                                             int lastFeatureID = Map.Instance.mapFeatures.features.Max (x => x.unique_id);
  766.                                             ObjectTag featureTag = new ()
  767.                                             {
  768.                                                 index = lastFeatureID,
  769.                                                 tag = new OSM_Tag (tag, sortedValue)
  770.                                             };
  771.                                             AddKnownTag (ref _knownTags, featureTag);
  772.                                             AddKnownTag (ref _knownFeatureTags, featureTag);
  773.                                             Map.Instance.mapFeatures.featureTags.Add (featureTag);
  774.                                             EditorUtility.SetDirty (Map.Instance.mapFeatures);
  775.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapFeatures);
  776.                                             break;
  777.                                         case 6:
  778.                                             Undo.RecordObject (Map.Instance.mapTrees, "Add tree entry");
  779.                                             int lastTreeID = Map.Instance.mapTrees.trees.Max (x => x.unique_id);
  780.                                             ObjectTag treeTag = new ()
  781.                                             {
  782.                                                 index = lastTreeID,
  783.                                                 tag = new OSM_Tag (tag, sortedValue)
  784.                                             };
  785.                                             AddKnownTag (ref _knownTags, treeTag);
  786.                                             AddKnownTag (ref _knownTreeTags, treeTag);
  787.                                             Map.Instance.mapTrees.treeTags.Add (treeTag);
  788.                                             EditorUtility.SetDirty (Map.Instance.mapTrees);
  789.                                             PrefabUtility.RecordPrefabInstancePropertyModifications (Map.Instance.mapTrees);
  790.                                             break;
  791.                                     }
  792.                                 }
  793.                             }
  794.                         }
  795.                         EditorGUILayout.EndHorizontal ();
  796.                     }
  797.                     EditorGUILayout.EndVertical ();
  798.                 }
  799.                 else
  800.                 {
  801.                     EditorGUILayout.EndHorizontal ();
  802.                 }
  803.             }
  804.             EditorGUILayout.EndVertical ();
  805.         }
  806.  
  807.         private void ClearKnownTags (string tag, string sortedValue, string alreadyTagged)
  808.         {
  809.             _tagDropdowns [tag].valueCounts [sortedValue] = 0;
  810.             _knownTags [tag].Remove (sortedValue);
  811.             if (_knownTags [tag].Count == 0)
  812.             {
  813.                 _knownTags.Remove (tag);
  814.             }
  815.  
  816.             switch (alreadyTagged)
  817.             {
  818.                 case "Surfaces":
  819.                     _knownSurfaceTags [tag].Remove (sortedValue);
  820.                     if (_knownSurfaceTags [tag].Count == 0)
  821.                     {
  822.                         _knownSurfaceTags.Remove (tag);
  823.                     }
  824.                     break;
  825.                 case "Roads":
  826.                     _knownRoadTags [tag].Remove (sortedValue);
  827.                     if (_knownRoadTags [tag].Count == 0)
  828.                     {
  829.                         _knownRoadTags.Remove (tag);
  830.                     }
  831.                     break;
  832.                 case "Buildings":
  833.                     _knownBuildingTags [tag].Remove (sortedValue);
  834.                     if (_knownBuildingTags [tag].Count == 0)
  835.                     {
  836.                         _knownBuildingTags.Remove (tag);
  837.                     }
  838.                     break;
  839.                 case "Entities":
  840.                     _knownEntityTags [tag].Remove (sortedValue);
  841.                     if (_knownEntityTags [tag].Count == 0)
  842.                     {
  843.                         _knownEntityTags.Remove (tag);
  844.                     }
  845.                     break;
  846.                 case "Features":
  847.                     _knownFeatureTags [tag].Remove (sortedValue);
  848.                     if (_knownFeatureTags [tag].Count == 0)
  849.                     {
  850.                         _knownFeatureTags.Remove (tag);
  851.                     }
  852.                     break;
  853.                 case "Trees":
  854.                     _knownTreeTags [tag].Remove (sortedValue);
  855.                     if (_knownTreeTags [tag].Count == 0)
  856.                     {
  857.                         _knownTreeTags.Remove (tag);
  858.                     }
  859.                     break;
  860.             }
  861.         }
  862.  
  863.         private void ExportCsvDataToClipboard (Database database)
  864.         {
  865.             StringBuilder sb = new ();
  866.             sb.AppendLine ("Type,Key,Value,Count");
  867.             foreach (KeyValuePair<string, TagCounts> node in _tagCountsNodes [database])
  868.             {
  869.                 foreach (KeyValuePair<string, int> value in node.Value.valueCounts)
  870.                 {
  871.                     sb.Append ("Node,");
  872.                     sb.Append (CleanText (node.Key));
  873.                     sb.Append (",");
  874.                     sb.Append (CleanText (value.Key));
  875.                     sb.Append (",");
  876.                     sb.AppendLine (value.Value.ToString ());
  877.                 }
  878.             }
  879.             foreach (KeyValuePair<string, TagCounts> relation in _tagCountsRelations [database])
  880.             {
  881.                 foreach (KeyValuePair<string, int> value in relation.Value.valueCounts)
  882.                 {
  883.                     sb.Append ("Relation,");
  884.                     sb.Append (CleanText (relation.Key));
  885.                     sb.Append (",");
  886.                     sb.Append (CleanText (value.Key));
  887.                     sb.Append (",");
  888.                     sb.AppendLine (value.Value.ToString ());
  889.                 }
  890.             }
  891.             foreach (KeyValuePair<string, TagCounts> way in _tagCountsWays [database])
  892.             {
  893.                 foreach (KeyValuePair<string, int> value in way.Value.valueCounts)
  894.                 {
  895.                     sb.Append ("Way,");
  896.                     sb.Append (CleanText (way.Key));
  897.                     sb.Append (",");
  898.                     sb.Append (CleanText (value.Key));
  899.                     sb.Append (",");
  900.                     sb.AppendLine (value.Value.ToString ());
  901.                 }
  902.             }
  903.             GUIUtility.systemCopyBuffer = sb.ToString ();
  904.         }
  905.  
  906.         private void OnInspectorUpdate ()
  907.         {
  908.             Repaint ();
  909.         }
  910.  
  911.         private static string CleanText (string input)
  912.         {
  913.             const string doubleQuotes = "\"";
  914.             const string doubleQuotesForExcel = "\"\"";
  915.             bool hasComma = input.IndexOf (',') >= 0;
  916.             bool hasDoubleQuotes = input.IndexOf ('"') >= 0;
  917.             string result = input.Replace ("\n", " ").Replace ("\r", " ").Trim ();
  918.             if (hasDoubleQuotes)
  919.             {
  920.                 result = result.Replace ("\"", doubleQuotesForExcel);
  921.                 return doubleQuotes + result + doubleQuotes;
  922.             }
  923.             if (hasComma)
  924.             {
  925.                 return doubleQuotes + result + doubleQuotes;
  926.             }
  927.             return result;
  928.         }
  929.  
  930.         private class TagCounts
  931.         {
  932.             public int count;
  933.             public bool showToggle;
  934.             public readonly Dictionary<string, int> valueCounts;
  935.  
  936.             public TagCounts (int count, Dictionary<string, int> valueCounts)
  937.             {
  938.                 this.count = count;
  939.                 showToggle = false;
  940.                 this.valueCounts = valueCounts;
  941.             }
  942.         }
  943.     }
  944. }
  945.  
  946. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement