protected override void Process(System.Web.HttpContext context, ResourceManager resourceManager) { context.Response.ContentType = ContentTypes.Text.Plain; context.Response.StatusCode = 200; string?sectorName = GetStringOption(context, "sector"); string?type = GetStringOption(context, "type"); string?regex = GetStringOption(context, "regex"); string?milieu = GetStringOption(context, "milieu"); // NOTE: This (re)initializes a static data structure used for // resolving names into sector locations, so needs to be run // before any other objects (e.g. Worlds) are loaded. SectorMap.Flush(); SectorMap map = SectorMap.GetInstance(resourceManager); var sectorQuery = from sector in map.Sectors where (sectorName == null || sector.Names[0].Text.StartsWith(sectorName, ignoreCase: true, culture: CultureInfo.InvariantCulture)) && (sector.DataFile != null) && (type == null || sector.DataFile.Type == type) && (!sector.Tags.Contains("ZCR")) && (!sector.Tags.Contains("meta")) && (milieu == null || sector.CanonicalMilieu == milieu) orderby sector.Names[0].Text select sector; Dictionary <string, HashSet <string> > codes = new Dictionary <string, HashSet <string> >(); Regex filter = new Regex(regex ?? ".*"); foreach (var sector in sectorQuery) { WorldCollection?worlds = sector.GetWorlds(resourceManager, cacheResults: false); if (worlds == null) { continue; } foreach (var code in worlds .SelectMany(world => world.Codes) .Where(code => filter.IsMatch(code) && !s_knownCodes.IsMatch(code))) { if (!codes.ContainsKey(code)) { codes.Add(code, new HashSet <string>()); } codes[code].Add($"{sector.Names[0].Text} [{sector.CanonicalMilieu}]"); } } foreach (var code in codes.Keys.OrderBy(s => s)) { context.Response.Output.Write(code + " - "); foreach (var sector in codes[code].OrderBy(s => s)) { context.Response.Output.Write(sector + " "); } context.Response.Output.WriteLine(""); } }
public override void Process(ResourceManager resourceManager) { // NOTE: This (re)initializes a static data structure used for // resolving names into sector locations, so needs to be run // before any other objects (e.g. Worlds) are loaded. SectorMap map = SectorMap.GetInstance(resourceManager); SendResult(map.GetMilieux().Select(code => new Results.Milieu(code)).ToList()); }
public override void Process() { ResourceManager resourceManager = new ResourceManager(context.Server); // NOTE: This (re)initializes a static data structure used for // resolving names into sector locations, so needs to be run // before any other objects (e.g. Worlds) are loaded. SectorMap map = SectorMap.GetInstance(resourceManager); // Filter parameters string milieu = GetStringOption("milieu") ?? GetStringOption("era"); bool requireData = GetBoolOption("requireData", defaultValue: false); string[] tags = GetStringsOption("tag"); UniverseResult data = new UniverseResult(); foreach (Sector sector in map.Sectors) { if (requireData && sector.DataFile == null) { continue; } if (sector.Tags.Contains("meta")) { continue; } if (milieu != null && sector.DataFile?.Milieu != milieu) { continue; } if (tags != null && !tags.Any(tag => sector.Tags.Contains(tag))) { continue; } data.Sectors.Add(new UniverseResult.SectorResult(sector)); } SendResult(context, data); }
protected override void Process(System.Web.HttpContext context) { ResourceManager resourceManager = new ResourceManager(context.Server); // NOTE: This (re)initializes a static data structure used for // resolving names into sector locations, so needs to be run // before any other objects (e.g. Worlds) are loaded. SectorMap map = SectorMap.GetInstance(resourceManager); context.Response.ContentType = MediaTypeNames.Text.Plain; context.Response.BufferOutput = false; foreach (Sector sector in map.Sectors) { WorldCollection worlds = sector.GetWorlds(resourceManager); if (worlds == null) { continue; } foreach (World world in worlds) { List <string> list = new List <string> { sector.Names[0].Text, sector.X.ToString(), sector.Y.ToString(), world.X.ToString(), world.Y.ToString(), world.Name, world.UWP, world.Bases, world.Remarks, world.PBG, world.Allegiance, world.Stellar }; WriteCSV(context.Response.Output, list); } } }
protected override void Process(System.Web.HttpContext context) { context.Response.ContentType = MediaTypeNames.Text.Plain; context.Response.BufferOutput = false; ResourceManager resourceManager = new ResourceManager(context.Server); string sectorName = GetStringOption(context, "sector"); string type = GetStringOption(context, "type"); string milieu = GetStringOption(context, "milieu"); string tag = GetStringOption(context, "tag"); // NOTE: This (re)initializes a static data structure used for // resolving names into sector locations, so needs to be run // before any other objects (e.g. Worlds) are loaded. SectorMap.Flush(); SectorMap map = SectorMap.GetInstance(resourceManager); var sectorQuery = from sector in map.Sectors where (sectorName == null || sector.Names[0].Text.StartsWith(sectorName, ignoreCase: true, culture: CultureInfo.InvariantCulture)) && (sector.DataFile != null) && (type == null || sector.DataFile.Type == type) && (milieu == null || sector.CanonicalMilieu == milieu) && (tag == null || sector.Tags.Contains(tag)) && (sector.Tags.Contains("OTU") || sector.Tags.Contains("Apocryphal") || sector.Tags.Contains("Faraway")) orderby sector.Names[0].Text select sector; foreach (var sector in sectorQuery) { context.Response.Output.WriteLine(sector.Names[0].Text); #if DEBUG WorldCollection worlds = sector.GetWorlds(resourceManager, cacheResults: false); if (worlds != null) { double pop = worlds.Select(w => w.Population).Sum(); if (pop > 0) { context.Response.Output.WriteLine($"{worlds.Count()} world(s) - population: {pop / 1e9:#,###.##} billion"); } else { context.Response.Output.WriteLine($"{worlds.Count()} world(s) - population: N/A"); } worlds.ErrorList.Report(context.Response.Output); } else { context.Response.Output.WriteLine("0 world(s)"); } foreach (IAllegiance item in sector.Borders.AsEnumerable <IAllegiance>() .Concat(sector.Routes.AsEnumerable <IAllegiance>()) .Concat(sector.Labels.AsEnumerable <IAllegiance>())) { if (string.IsNullOrWhiteSpace(item.Allegiance)) { continue; } if (sector.GetAllegianceFromCode(item.Allegiance) == null) { context.Response.Output.WriteLine($"Undefined allegiance code: {item.Allegiance} (on {item.GetType().Name})"); } } foreach (var route in sector.Routes) { System.Drawing.Point startSector = sector.Location, endSector = sector.Location; startSector.Offset(route.StartOffset); endSector.Offset(route.EndOffset); Location startLocation = new Location(startSector, route.Start); Location endLocation = new Location(endSector, route.End); int distance = Astrometrics.HexDistance(Astrometrics.LocationToCoordinates(startLocation), Astrometrics.LocationToCoordinates(endLocation)); if (distance == 0) { context.Response.Output.WriteLine($"Error: Route length {distance}: {route.ToString()}"); } else if (distance > 4) { context.Response.Output.WriteLine($"Warning: Route length {distance}: {route.ToString()}"); } /* * This fails because of routes that use e.g. 3341-style coordinates * It will also be extremely slow due to loading world lists w/o caching * { * var w = map.FromLocation(startSector).GetWorlds(resourceManager, cacheResults: false); * if (w != null) * { * if (w[route.StartPoint.X, route.StartPoint.Y] == null) * context.Response.Output.WriteLine($"Route start empty hex: {route.ToString()}"); * } * } * { * var w = map.FromLocation(endSector).GetWorlds(resourceManager, cacheResults: false); * if (w != null) * { * if (w[route.EndPoint.X, route.EndPoint.Y] == null) * context.Response.Output.WriteLine($"Route end empty hex: {route.ToString()}"); * } * } */ } #endif context.Response.Output.WriteLine(); } return; }
protected override void Process(System.Web.HttpContext context, ResourceManager resourceManager) { context.Response.ContentType = ContentTypes.Text.Plain; context.Response.BufferOutput = false; string sectorName = GetStringOption(context, "sector"); string type = GetStringOption(context, "type"); string milieu = GetStringOption(context, "milieu"); string tag = GetStringOption(context, "tag"); bool hide_tl = GetBoolOption(context, "hide-tl"); bool hide_gov = GetBoolOption(context, "hide-gov"); bool hide_stellar = GetBoolOption(context, "hide-stellar"); ErrorLogger.Severity severity = GetBoolOption(context, "warnings", true) ? 0 : ErrorLogger.Severity.Error; // NOTE: This (re)initializes a static data structure used for // resolving names into sector locations, so needs to be run // before any other objects (e.g. Worlds) are loaded. SectorMap.Flush(); SectorMap map = SectorMap.GetInstance(resourceManager); var sectorQuery = from sector in map.Sectors where (sectorName == null || sector.Names[0].Text.StartsWith(sectorName, ignoreCase: true, culture: CultureInfo.InvariantCulture)) && (sector.DataFile != null) && (type == null || sector.DataFile.Type == type) && (milieu == null || sector.CanonicalMilieu == milieu) && (tag == null || sector.Tags.Contains(tag)) && (sector.Tags.Contains("OTU") || sector.Tags.Contains("Apocryphal") || sector.Tags.Contains("Faraway")) orderby sector.Names[0].Text select sector; foreach (var sector in sectorQuery) { context.Response.Output.WriteLine($"{sector.Names[0].Text} - {sector.Milieu}"); #if DEBUG int error_count = 0; int warning_count = 0; try { WorldCollection worlds = sector.GetWorlds(resourceManager, cacheResults: false); if (worlds != null) { double pop = worlds.Select(w => w.Population).Sum(); if (pop > 0) { context.Response.Output.WriteLine($"{worlds.Count()} world(s) - population: {pop / 1e9:#,###.##} billion"); } else { context.Response.Output.WriteLine($"{worlds.Count()} world(s) - population: N/A"); } worlds.ErrorList.Report(context.Response.Output, severity, (ErrorLogger.Record record) => { if (hide_gov && (record.message.StartsWith("UWP: Gov") || record.message.StartsWith("Gov"))) { return(false); } if (hide_tl && record.message.StartsWith("UWP: TL")) { return(false); } if (hide_stellar && record.message.StartsWith("Invalid stellar data:")) { return(false); } return(true); }); error_count += worlds.ErrorList.CountOf(ErrorLogger.Severity.Error); warning_count += worlds.ErrorList.CountOf(ErrorLogger.Severity.Warning); } else { context.Response.Output.WriteLine("0 world(s)"); } } catch (ParseException ex) { context.Response.Output.WriteLine($"Bad data file: {ex.Message}"); } foreach (IAllegiance item in sector.Borders.AsEnumerable <IAllegiance>() .Concat(sector.Routes.AsEnumerable <IAllegiance>()) .Concat(sector.Labels.AsEnumerable <IAllegiance>())) { if (string.IsNullOrWhiteSpace(item.Allegiance)) { continue; } if (sector.GetAllegianceFromCode(item.Allegiance) == null) { context.Response.Output.WriteLine($"Undefined allegiance code: {item.Allegiance} (on {item.GetType().Name})"); } } foreach (var route in sector.Routes) { System.Drawing.Point startSector = sector.Location, endSector = sector.Location; startSector.Offset(route.StartOffset); endSector.Offset(route.EndOffset); int distance = Astrometrics.HexDistance( Astrometrics.LocationToCoordinates(new Location(startSector, route.Start)), Astrometrics.LocationToCoordinates(new Location(endSector, route.End))); if (distance == 0) { context.Response.Output.WriteLine($"Error: Route length {distance}: {route.ToString()}"); ++error_count; } else if (distance > 4) { if (severity <= ErrorLogger.Severity.Warning) { context.Response.Output.WriteLine($"Warning: Route length {distance}: {route.ToString()}"); } ++warning_count; } /* * This fails because of routes that use e.g. 3341-style coordinates * It will also be extremely slow due to loading world lists w/o caching * { * var w = map.FromLocation(startSector).GetWorlds(resourceManager, cacheResults: false); * if (w != null) * { * if (w[route.StartPoint.X, route.StartPoint.Y] == null) * context.Response.Output.WriteLine($"Route start empty hex: {route.ToString()}"); * } * } * { * var w = map.FromLocation(endSector).GetWorlds(resourceManager, cacheResults: false); * if (w != null) * { * if (w[route.EndPoint.X, route.EndPoint.Y] == null) * context.Response.Output.WriteLine($"Route end empty hex: {route.ToString()}"); * } * } */ } context.Response.Output.WriteLine($"{error_count} errors, {warning_count} warnings."); #endif context.Response.Output.WriteLine(); } return; }
public static void PopulateDatabase(ResourceManager resourceManager, Action <string> statusCallback) { // Lock to prevent indexing twice, without blocking tile requests. lock (SearchEngine.s_lock) { // NOTE: This (re)initializes a static data structure used for // resolving names into sector locations, so needs to be run // before any other objects (e.g. Worlds) are loaded. SectorMap map = SectorMap.GetInstance(resourceManager); using (var connection = DBUtil.MakeConnection()) { SqlCommand sqlCommand; // // Repopulate the tables - locally first // FUTURE: will need to batch this up rather than keep it in memory! // DataTable dt_sectors = new DataTable(); for (int i = 0; i < SECTORS_COLUMNS.Length; ++i) { dt_sectors.Columns.Add(new DataColumn()); } DataTable dt_subsectors = new DataTable(); for (int i = 0; i < SUBSECTORS_COLUMNS.Length; ++i) { dt_subsectors.Columns.Add(new DataColumn()); } DataTable dt_worlds = new DataTable(); for (int i = 0; i < WORLDS_COLUMNS.Length; ++i) { dt_worlds.Columns.Add(new DataColumn()); } DataTable dt_labels = new DataTable(); for (int i = 0; i < LABELS_COLUMNS.Length; ++i) { dt_labels.Columns.Add(new DataColumn()); } // Map of (milieu, string) => [ points ... ] Dictionary <Tuple <string, string>, List <Point> > labels = new Dictionary <Tuple <string, string>, List <Point> >(); Action <string, string, Point> AddLabel = (string milieu, string text, Point coords) => { if (text == null) { return; } text = SanifyLabel(text); var key = Tuple.Create(milieu, text); if (!labels.ContainsKey(key)) { labels.Add(key, new List <Point>()); } labels[key].Add(coords); }; statusCallback("Parsing data..."); foreach (Sector sector in map.Sectors) { // TODO: Index alternate milieu if (!sector.Tags.Contains("OTU") && !sector.Tags.Contains("Faraway")) { continue; } foreach (Name name in sector.Names) { DataRow row = dt_sectors.NewRow(); row.ItemArray = new object[] { sector.CanonicalMilieu, sector.X, sector.Y, name.Text }; dt_sectors.Rows.Add(row); } if (!string.IsNullOrEmpty(sector.Abbreviation)) { DataRow row = dt_sectors.NewRow(); row.ItemArray = new object[] { sector.CanonicalMilieu, sector.X, sector.Y, sector.Abbreviation }; dt_sectors.Rows.Add(row); } foreach (Subsector subsector in sector.Subsectors) { DataRow row = dt_subsectors.NewRow(); row.ItemArray = new object[] { sector.CanonicalMilieu, sector.X, sector.Y, subsector.Index, subsector.Name }; dt_subsectors.Rows.Add(row); } foreach (Border border in sector.Borders.Where(b => b.ShowLabel)) { AddLabel( sector.CanonicalMilieu, border.GetLabel(sector), Astrometrics.LocationToCoordinates(new Location(sector.Location, border.LabelPosition))); } foreach (Label label in sector.Labels) { AddLabel( sector.CanonicalMilieu, label.Text, Astrometrics.LocationToCoordinates(new Location(sector.Location, label.Hex))); } #if DEBUG if (!sector.Selected) { continue; } #endif // NOTE: May need to page this at some point WorldCollection worlds = sector.GetWorlds(resourceManager, cacheResults: false); if (worlds == null) { continue; } var world_query = from world in worlds where !world.IsPlaceholder select world; foreach (World world in world_query) { DataRow row = dt_worlds.NewRow(); row.ItemArray = new object[] { sector.CanonicalMilieu, world.Coordinates.X, world.Coordinates.Y, sector.X, sector.Y, world.X, world.Y, string.IsNullOrEmpty(world.Name) ? (object)DBNull.Value : (object)world.Name, world.UWP, world.Remarks, world.PBG, string.IsNullOrEmpty(world.Zone) ? "G" : world.Zone, world.Allegiance, sector.Names.Count > 0 ? (object)sector.Names[0] : (object)DBNull.Value }; dt_worlds.Rows.Add(row); } } foreach (KeyValuePair <Tuple <string, string>, List <Point> > entry in labels) { string milieu = entry.Key.Item1; string name = entry.Key.Item2; List <Point> points = entry.Value; Point avg = new Point( (int)Math.Round(points.Select(p => p.X).Average()), (int)Math.Round(points.Select(p => p.Y).Average())); Point min = new Point(points.Select(p => p.X).Min(), points.Select(p => p.Y).Min()); Point max = new Point(points.Select(p => p.X).Max(), points.Select(p => p.Y).Max()); Size size = new Size(max.X - min.X, max.Y - min.Y); int radius = Math.Max(size.Width, size.Height); DataRow row = dt_labels.NewRow(); row.ItemArray = new object[] { milieu, avg.X, avg.Y, radius, name }; dt_labels.Rows.Add(row); } // // Rebuild the tables with fresh schema // const string INDEX_OPTIONS = " WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)"; const string DROP_TABLE_IF_EXISTS = "IF EXISTS(SELECT 1 FROM sys.objects WHERE OBJECT_ID = OBJECT_ID(N'{0}') AND type = (N'U')) DROP TABLE {0}"; string[] rebuild_schema = { string.Format(DROP_TABLE_IF_EXISTS, "sectors"), "CREATE TABLE sectors (" + string.Join(",", SECTORS_COLUMNS) + ")", "CREATE NONCLUSTERED INDEX sector_name ON sectors ( name ASC )" + INDEX_OPTIONS, "CREATE NONCLUSTERED INDEX sector_milieu ON sectors ( milieu ASC )" + INDEX_OPTIONS, string.Format(DROP_TABLE_IF_EXISTS, "subsectors"), "CREATE TABLE subsectors (" + string.Join(",", SUBSECTORS_COLUMNS) + ")", "CREATE NONCLUSTERED INDEX subsector_name ON subsectors ( name ASC )" + INDEX_OPTIONS, "CREATE NONCLUSTERED INDEX subsector_milieu ON subsectors ( milieu ASC )" + INDEX_OPTIONS, string.Format(DROP_TABLE_IF_EXISTS, "worlds"), "CREATE TABLE worlds (" + string.Join(",", WORLDS_COLUMNS) + ")", "CREATE NONCLUSTERED INDEX world_name ON worlds ( name ASC )" + INDEX_OPTIONS, "CREATE NONCLUSTERED INDEX world_uwp ON worlds ( uwp ASC )" + INDEX_OPTIONS, "CREATE NONCLUSTERED INDEX world_pbg ON worlds ( pbg ASC )" + INDEX_OPTIONS, "CREATE NONCLUSTERED INDEX world_alleg ON worlds ( alleg ASC )" + INDEX_OPTIONS, "CREATE NONCLUSTERED INDEX world_sector_name ON worlds ( sector_name ASC )" + INDEX_OPTIONS, "CREATE NONCLUSTERED INDEX world_milieu ON worlds ( milieu ASC )" + INDEX_OPTIONS, string.Format(DROP_TABLE_IF_EXISTS, "labels"), "CREATE TABLE labels (" + string.Join(",", LABELS_COLUMNS) + ")", "CREATE NONCLUSTERED INDEX name ON labels ( name ASC )" + INDEX_OPTIONS, "CREATE NONCLUSTERED INDEX milieu ON labels ( milieu ASC )" + INDEX_OPTIONS, }; statusCallback("Rebuilding schema..."); foreach (string cmd in rebuild_schema) { sqlCommand = new SqlCommand(cmd, connection); sqlCommand.ExecuteNonQuery(); } // // And shovel the data into the database en masse // Action <string, DataTable, int> BulkInsert = (string name, DataTable table, int batchSize) => { using (var bulk = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, null)) { statusCallback($"Writing {table.Rows.Count} {name}..."); bulk.BatchSize = batchSize; bulk.DestinationTableName = name; bulk.WriteToServer(table); } }; BulkInsert("sectors", dt_sectors, dt_sectors.Rows.Count); BulkInsert("subsectors", dt_subsectors, dt_subsectors.Rows.Count); BulkInsert("worlds", dt_worlds, 4096); BulkInsert("labels", dt_labels, dt_labels.Rows.Count); } statusCallback("Complete!"); } }