DynamicBibleUtility - Adding initial geolocation JSON creation.

Still need to incorporate Strong's numbers.
This commit is contained in:
Jacob Pike 2017-03-04 17:42:33 -05:00
parent 8fb3388392
commit b67e52950a
10 changed files with 664 additions and 16 deletions

View File

@ -63,6 +63,12 @@
<Compile Include="frmMain.Designer.cs">
<DependentUpon>frmMain.cs</DependentUpon>
</Compile>
<Compile Include="Geolocation\BibleBook.cs" />
<Compile Include="Geolocation\BibleLocationIndexByName.cs" />
<Compile Include="Geolocation\BibleLocationIndexByVerse.cs" />
<Compile Include="Geolocation\BibleLocationReference.cs" />
<Compile Include="Geolocation\BibleVerseReference.cs" />
<Compile Include="Geolocation\OpenBibleDotInfoLocationParser.cs" />
<Compile Include="Helpers.cs" />
<Compile Include="Index.cs" />
<Compile Include="JSON.cs" />

View File

@ -0,0 +1,177 @@
using System.Collections.Generic;
namespace DynamicBibleUtility.Geolocation
{
/// <summary>
/// An enum for uniquely identifying a book of the Bible.
/// An "invalid" enum value is first so that the numeric
/// value for actual books starts at 1, which helps maintain
/// consistency with how verse references are used in other
/// data for the app.
/// </summary>
public enum BibleBookId
{
INVALID,
GENESIS,
EXODUS,
LEVITICUS,
NUMBERS,
DEUTERONOMY,
JOSHUA,
JUDGES,
RUTH,
FIRST_SAMUEL,
SECOND_SAMUEL,
FIRST_KINGS,
SECOND_KINGS,
FIRST_CHRONICLES,
SECOND_CHRONICLES,
EZRA,
NEHEMIAH,
ESTHER,
JOB,
PSALMS,
PROVERBS,
ECCLESIASTES,
SONG_OF_SOLOMON,
ISAIAH,
JEREMIAH,
LAMENTATIONS,
EZEKIEL,
DANIEL,
HOSEA,
JOEL,
AMOS,
OBADIAH,
JONAH,
MICAH,
NAHUM,
HABAKKUK,
ZEPHANIAH,
HAGGAI,
ZECHARIAH,
MALACHI,
MATTHEW,
MARK,
LUKE,
JOHN,
ACTS,
ROMANS,
FIRST_CORINTHIANS,
SECOND_CORINTHIANS,
GALATIANS,
EPHESIANS,
PHILIPPIANS,
COLOSSIANS,
FIRST_THESSALONIANS,
SECOND_THESSALONIANS,
FIRST_TIMOTHY,
SECOND_TIMOTHY,
TITUS,
PHILEMON,
HEBREWS,
JAMES,
FIRST_PETER,
SECOND_PETER,
FIRST_JOHN,
SECOND_JOHN,
THIRD_JOHN,
JUDE,
REVELATION
};
/// <summary>
/// A book of the Bible.
/// </summary>
public class BibleBook
{
/// <summary>The unique ID of book.</summary>
public BibleBookId Id = BibleBookId.INVALID;
/// <summary>
/// Constructs a book by parsing a string.
/// </summary>
/// <param name="book_string">A string representation of the book.</param>
/// <exception cref="System.Exception">Thrown if a parsing error occurs.</exception>
public BibleBook(string book_string)
{
// DEFINE A MAPPING OF BOOK STRINGS TO BOOK IDs.
// This code currently only handles parsing a subset of book strings as
// needed for geolocation parsing. If needed, it could be made more
// generic later and moved out of this namespace, but the structure
// of this logic would need to be altered.
var string_to_book_id_lookup = new Dictionary<string, BibleBookId>
{
{ "Gen", BibleBookId.GENESIS },
{ "Ex", BibleBookId.EXODUS },
{ "Lev", BibleBookId.LEVITICUS },
{ "Num", BibleBookId.NUMBERS },
{ "Deut", BibleBookId.DEUTERONOMY },
{ "Josh", BibleBookId.JOSHUA },
{ "Judg", BibleBookId.JUDGES },
{ "Ruth", BibleBookId.RUTH },
{ "1 Sam", BibleBookId.FIRST_SAMUEL },
{ "2 Sam", BibleBookId.SECOND_SAMUEL },
{ "1 Kgs", BibleBookId.FIRST_KINGS },
{ "2 Kgs", BibleBookId.SECOND_KINGS },
{ "1 Chr", BibleBookId.FIRST_CHRONICLES },
{ "2 Chr", BibleBookId.SECOND_CHRONICLES },
{ "Ezra", BibleBookId.EZRA },
{ "Neh", BibleBookId.NEHEMIAH },
{ "Est", BibleBookId.ESTHER },
{ "Job", BibleBookId.JOB },
{ "Ps", BibleBookId.PSALMS },
// No locations exist for Proverbs. { "", BibleBookId.PROVERBS },
{ "Eccl", BibleBookId.ECCLESIASTES },
{ "Sng", BibleBookId.SONG_OF_SOLOMON },
{ "Isa", BibleBookId.ISAIAH },
{ "Jer", BibleBookId.JEREMIAH },
{ "Lam", BibleBookId.LAMENTATIONS },
{ "Ezek", BibleBookId.EZEKIEL },
{ "Dan", BibleBookId.DANIEL },
{ "Hos", BibleBookId.HOSEA },
{ "Joel", BibleBookId.JOEL },
{ "Amos", BibleBookId.AMOS },
{ "Obad", BibleBookId.OBADIAH },
{ "Jonah", BibleBookId.JONAH },
{ "Mic", BibleBookId.MICAH },
{ "Nahum", BibleBookId.NAHUM },
{ "Hab", BibleBookId.HABAKKUK },
{ "Zeph", BibleBookId.ZEPHANIAH },
{ "Hag", BibleBookId.HAGGAI },
{ "Zech", BibleBookId.ZECHARIAH },
{ "Mal", BibleBookId.MALACHI },
{ "Matt", BibleBookId.MATTHEW },
{ "Mark", BibleBookId.MARK },
{ "Luke", BibleBookId.LUKE },
{ "John", BibleBookId.JOHN },
{ "Acts", BibleBookId.ACTS },
{ "Rom", BibleBookId.ROMANS },
{ "1 Cor", BibleBookId.FIRST_CORINTHIANS },
{ "2 Cor", BibleBookId.SECOND_CORINTHIANS },
{ "Gal", BibleBookId.GALATIANS },
{ "Eph", BibleBookId.EPHESIANS },
{ "Phil", BibleBookId.PHILIPPIANS },
{ "Col", BibleBookId.COLOSSIANS },
{ "1 Thes", BibleBookId.FIRST_THESSALONIANS },
{ "2 Thes", BibleBookId.SECOND_THESSALONIANS },
{ "1 Tim", BibleBookId.FIRST_TIMOTHY },
{ "2 Tim", BibleBookId.SECOND_TIMOTHY },
{ "Titus", BibleBookId.TITUS },
// No locations exist for Philemon. { "", BibleBookId.PHILEMON },
{ "Heb", BibleBookId.HEBREWS },
// No locations exist for James. { "", BibleBookId.JAMES },
{ "1 Pet", BibleBookId.FIRST_PETER },
{ "2 Pet", BibleBookId.SECOND_PETER },
// No locations exist for 1 John. { "", BibleBookId.FIRST_JOHN },
// No locations exist for 2 John. { "", BibleBookId.SECOND_JOHN },
// No locations exist for 3 John. { "", BibleBookId.THIRD_JOHN },
{ "Jude", BibleBookId.JUDE },
{ "Rev", BibleBookId.REVELATION }
};
// GET THE BOOK ID FROM THE STRING.
this.Id = string_to_book_id_lookup[book_string];
}
}
}

View File

@ -0,0 +1,32 @@
using System.Collections.Generic;
namespace DynamicBibleUtility.Geolocation
{
/// <summary>
/// An index of Bible location data by location name.
/// </summary>
public class BibleLocationIndexByName
{
/// <summary>A mapping of location names to full location data.</summary>
public IDictionary<string, BibleLocationReference> NameToLocationLookup = new Dictionary<string, BibleLocationReference>();
/// <summary>
/// Creates the index from the provided locations.
/// </summary>
/// <param name="locations">The locations to put in the index.</param>
/// <exception cref="System.NullReferenceException">Thrown if the locations are null.</exception>
public BibleLocationIndexByName(IEnumerable<BibleLocationReference> locations)
{
// INDEX THE LOCATIONS BY NAME.
foreach (var location in locations)
{
// ONLY INDEX THE LOCATION IF IT HAS GEOGRAPHIC COORDINATES.
// The information currently isn't useful without these coordinates.
if (location.HasGeographicCoordinates)
{
NameToLocationLookup[location.Name] = location;
}
}
}
}
}

View File

@ -0,0 +1,52 @@
using System.Collections.Generic;
namespace DynamicBibleUtility.Geolocation
{
/// <summary>
/// An index of Bible location data by verse reference.
/// </summary>
public class BibleLocationIndexByVerse
{
/// <summary>
/// A mapping of verse references to location names.
/// The verse reference keys are stored using their short string form in order
/// to simplify hashing (no custom hash code implementation) and to simplify
/// JSON serialization (so that the short string form is only serialized).
/// Only the location name, rather than the full location data, is stored
/// since the name can be used in a parallel lookup in the <see cref="BibleLocationIndexByName"/>.
/// </summary>
public IDictionary<string, List<string>> VerseToLocationNameLookup = new Dictionary<string, List<string>>();
/// <summary>
/// Creates the index from the provided locations.
/// </summary>
/// <param name="locations">The locations to put in the index.</param>
/// <exception cref="System.NullReferenceException">Thrown if the locations are null.</exception>
public BibleLocationIndexByVerse(IEnumerable<BibleLocationReference> locations)
{
// INDEX THE LOCATIONS BY VERSE REFERENCE.
foreach (var location in locations)
{
// ONLY INDEX THE LOCATION IF IT HAS GEOGRAPHIC COORDINATES.
// The information currently isn't useful without these coordinates.
if (location.HasGeographicCoordinates)
{
// INDEX THE LOCATION NAME BY ALL ITS VERSE REFERENCES.
foreach (var verse_reference in location.VerseReferences)
{
// MAKE SURE THE VERSE HAS AN EXISTING COLLECTION FOR LOCATION DATA.
string verse_reference_string = verse_reference.ToString();
bool verse_reference_exists_in_index = VerseToLocationNameLookup.ContainsKey(verse_reference_string);
if (!verse_reference_exists_in_index)
{
VerseToLocationNameLookup[verse_reference_string] = new List<string>();
}
// ADD THE LOCATION NAME FOR THE VERSE.
VerseToLocationNameLookup[verse_reference_string].Add(location.Name);
}
}
}
}
}
}

View File

@ -0,0 +1,61 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace DynamicBibleUtility.Geolocation
{
/// <summary>
/// A location mentioned in the Bible, with geographic coordinates and verse references.
///
/// Attributes are used to control serialization to minimize the amount of JSON that
/// gets serialized.
/// </summary>
public class BibleLocationReference
{
/// <summary>
/// The name of the location.
/// Ignored for serialization since the name is more useful separately
/// as a key for a property in a JavaScript object, rather than
/// being duplicated again in the serialized data.
/// </summary>
[JsonIgnore]
public string Name = "";
/// <summary>The latitude of the location, if available.</summary>
[JsonProperty("lat")]
public double? Latitude = null;
/// <summary>The longitude of the location, if available.</summary>
[JsonProperty("lon")]
public double? Longitude = null;
/// <summary>References to verses that mention the location.</summary>
[JsonIgnore]
public IEnumerable<BibleVerseReference> VerseReferences = new List<BibleVerseReference>();
/// <summary>
/// Gets references to verses that mention the location, in their short string form
/// as used by the Dynamic Bible app.
/// </summary>
[JsonProperty("vss")]
public IEnumerable<string> VerseReferenceStrings
{
get
{
var verse_reference_strings = VerseReferences.Select(verse_reference => verse_reference.ToString());
return verse_reference_strings;
}
}
/// <summary>
/// True if this location information has full geographic coordinates; false otherwise.
/// Marked such that it doesn't get serialized to JSON.
/// </summary>
[JsonIgnore]
public bool HasGeographicCoordinates
{
get
{
bool has_geographic_coordinates = Latitude.HasValue && Longitude.HasValue;
return has_geographic_coordinates;
}
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using Newtonsoft.Json;
namespace DynamicBibleUtility.Geolocation
{
/// <summary>
/// A reference to a Bible verse including a book, chapter, and verse.
/// </summary>
public class BibleVerseReference
{
/// <summary>The book of the Bible.</summary>
public BibleBook Book = null;
/// <summary>The chapter number within the book.</summary>
public int Chapter = 0;
/// <summary>The verse number within the book.</summary>
public int Verse = 0;
/// <summary>
/// Converts the verse reference to the canonical string form
/// for the Dynamic Bible app. This form has the numeric book,
/// chapter, and verse in a single colon-separated string.
/// </summary>
/// <returns>The canonical short string form of the verse reference.</returns>
public override string ToString()
{
int book_number = Convert.ToInt32(Book.Id);
string short_reference_string = $"{book_number}:{Chapter}:{Verse}";
return short_reference_string;
}
}
}

View File

@ -0,0 +1,187 @@
using System;
using System.Collections.Generic;
using System.IO;
/// <summary>
/// A namespace for code related to geolocation data for the Dynamic Bible app.
/// </summary>
namespace DynamicBibleUtility.Geolocation
{
/// <summary>
/// A parser for Biblical location information from openbible.info.
///
/// Specifically, this class only handles parsing the tab-delimited
/// file from https://www.openbible.info/geo/data/merged.txt.
/// Parsing this specific file was chosen over the KMZ/KML files
/// because it was a much simpler way to get the relevant data.
/// The "merged" version of the raw data was chosen over the
/// "unmerged" version because it seemed to contain more data.
///
/// The data parsed by this parser is licensed under the
/// Creative Commons Attribution license (see
/// https://www.openbible.info/geo/ and
/// https://creativecommons.org/licenses/by/4.0/).
/// </summary>
public class OpenBibleDotInfoLocationParser
{
/// <summary>
/// Parses Biblical location information from the specified file.
/// </summary>
/// <param name="filepath">The relative or absolute path to the file to parse.</param>
/// <returns>Location references parsed from the file; never null.</returns>
/// <exception cref="Exception">Thrown if a parsing error occurs.</exception>
public static IEnumerable<BibleLocationReference> Parse(string filepath)
{
// READ THE ENTIRE GEOLOCATION DATA FILE.
// It is small enough to store completely in memory.
string[] geolocation_input_file_lines = File.ReadAllLines(filepath);
// PARSE EACH LINE OF GEOLOCATION DATA.
// The first line contains a comment and the second line contains a header,
// so those two lines can be skipped.
const int FIRST_GEOLOCATION_LINE_INDEX = 2;
var locations = new List<BibleLocationReference>();
for (int line_index = FIRST_GEOLOCATION_LINE_INDEX; line_index < geolocation_input_file_lines.Length; ++line_index)
{
// SPLIT THE LINE INTO SEPARATE FIELDS.
// Since empty fields sometimes exist in the actual data, empty entries are still included
// from the string splitting operation to make indexing into known fields simpler.
const char FIELD_SEPARATOR = '\t';
string current_geolocation_line = geolocation_input_file_lines[line_index];
string[] current_line_fields = current_geolocation_line.Split(
new char[] { FIELD_SEPARATOR },
StringSplitOptions.None);
// PARSE THE LOCATION INFORMATION FROM CURRENT LINE.
BibleLocationReference location = new BibleLocationReference();
// The name is converted to lowercase to make it easier to do
// case insensitive lookups.
const int BIBLE_LOCATION_NAME_FIELD_INDEX = 0;
location.Name = current_line_fields[BIBLE_LOCATION_NAME_FIELD_INDEX];
location.Name = location.Name.ToLower();
// The file contains both the name of the location as mentioned in the Bible (parsed above)
// and this second name for the actual location that the geographic coordinates reference.
// Since the geographics coordinates are expected to be close enough to the Biblical name
// and the primary purpose of this data is to cross-reference the Biblical text,
// this second name is silently ignored but could be added later if desired.
const int GEO_COORDINATE_LOCATION_NAME_FIELD_INDEX = 1;
string geo_coordinate_location_name = current_line_fields[GEO_COORDINATE_LOCATION_NAME_FIELD_INDEX];
const int LATITUDE_INDEX = 2;
string latitude_string = current_line_fields[LATITUDE_INDEX];
location.Latitude = ParseGeographicCoordinate(latitude_string);
const int LONGITUDE_INDEX = 3;
string longitude_string = current_line_fields[LONGITUDE_INDEX];
location.Longitude = ParseGeographicCoordinate(longitude_string);
const int VERSE_REFERENCES_INDEX = 4;
string verse_references_csv_list = current_line_fields[VERSE_REFERENCES_INDEX];
location.VerseReferences = ParseVerseReferences(verse_references_csv_list);
// ADD THE LOCATION INFORMATION FOR RETURNING.
locations.Add(location);
}
return locations;
}
/// <summary>
/// Attempts to parse a geographic coordinate from the specified string.
/// This method is necessary because not all coordinate values in the file
/// are necessarily completely numeric.
/// </summary>
/// <param name="coordinate_string">The coordinate string to parse.</param>
/// <returns>
/// The geographic coordinate, if successfully parsed.
/// Null only if no geographic coordinate exists (an exception is thrown
/// if an unexpected parsing error occurs in order to provide easier visibilty
/// into such errors).
/// </returns>
/// <exception cref="Exception">Thrown if a parsing error occurs.</exception>
private static double? ParseGeographicCoordinate(string coordinate_string)
{
// REMOVE ANY KNOWN NON-NUMERIC CHARACTERS FROM THE STRING.
// These characters are used to mark cases where the location isn't known
// or the location may not be exact. That exactness isn't super important
// in this context, so the "marker" characters are ignored.
string numeric_coordinate_string = coordinate_string.Trim('?', '~', '<', '>');
// A '-' is used sometimes to indicate no location. Since a '-' could also
// be used for a negative geographic coordinate, it can only be safely
// trimmed from the end.
numeric_coordinate_string = numeric_coordinate_string.TrimEnd('-');
// CHECK IF A COORDINATE EXISTS.
bool coordinate_exists = !string.IsNullOrWhiteSpace(numeric_coordinate_string);
if (!coordinate_exists)
{
// Not all locations in this file may have geographic coordinates.
return null;
}
// PARSE THE NUMERIC COORDINATE.
double coordinate = double.Parse(numeric_coordinate_string);
return coordinate;
}
/// <summary>
/// Attempts to parse Bible verse references from a CSV list.
/// </summary>
/// <param name="verse_references_csv_list">A CSV list of Bible verse references.
/// Each reference is expected to be separated by a comma OR a comma and single space.</param>
/// <returns>The verse references from the string; an empty list if no verse references exist in the string.</returns>
/// <exception cref="Exception">Thrown if a parsing error occurs.</exception>
private static IEnumerable<BibleVerseReference> ParseVerseReferences(string verse_references_csv_list)
{
// GET THE INDIVIDUAL VERSE REFERENCE STRINGS FROM THE LIST.
string[] verse_reference_strings = verse_references_csv_list.Split(
new string[] { ", ", "," },
StringSplitOptions.RemoveEmptyEntries);
// PARSE EACH VERSE REFERENCE.
var verse_references = new List<BibleVerseReference>();
foreach (string verse_reference_string in verse_reference_strings)
{
// PARSE THE BOOK.
// A single space separates the book from the chapter and verse numbers.
// Since there might be an additional space before that separator
// for books with numbers at the start, a split can't be used directly.
const int BOOK_START_INDEX = 0;
int index_of_space_after_book = verse_reference_string.LastIndexOf(' ');
int book_string_length_in_characters = index_of_space_after_book;
string book_string = verse_reference_string.Substring(BOOK_START_INDEX, book_string_length_in_characters);
BibleBook book = new BibleBook(book_string);
// PARSE THE CHAPTER.
// A single colon separates the chapter and verse numbers.
int chapter_start_index = index_of_space_after_book + 1;
string chapter_and_verse_string = verse_reference_string.Substring(chapter_start_index);
string[] chapter_and_verse_numbers = chapter_and_verse_string.Split(
new char[] { ':' },
StringSplitOptions.RemoveEmptyEntries);
const int CHAPTER_INDEX = 0;
string chapter_string = chapter_and_verse_numbers[CHAPTER_INDEX];
int chapter = int.Parse(chapter_string);
// PARSE THE VERSE.
const int VERSE_INDEX = 1;
string verse_string = chapter_and_verse_numbers[VERSE_INDEX];
int verse = int.Parse(verse_string);
// ADD THE PARSED THE BIBLE VERSE REFERENCE.
BibleVerseReference verse_reference = new BibleVerseReference
{
Book = book,
Chapter = chapter,
Verse = verse
};
verse_references.Add(verse_reference);
}
return verse_references;
}
}
}

View File

@ -28,6 +28,7 @@
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.btnCreateIndex = new System.Windows.Forms.Button();
this.txtStatus = new System.Windows.Forms.TextBox();
this.btnCreateText = new System.Windows.Forms.Button();
@ -35,13 +36,16 @@
this.btnCreateStrongsDict = new System.Windows.Forms.Button();
this.btnCreateRMAC = new System.Windows.Forms.Button();
this.btnRmacCrossRefs = new System.Windows.Forms.Button();
this.btnCreateGeolocationJson = new System.Windows.Forms.Button();
this.createGeolocationJsonTooltip = new System.Windows.Forms.ToolTip(this.components);
this.SuspendLayout();
//
// btnCreateIndex
//
this.btnCreateIndex.Location = new System.Drawing.Point(12, 12);
this.btnCreateIndex.Location = new System.Drawing.Point(16, 15);
this.btnCreateIndex.Margin = new System.Windows.Forms.Padding(4);
this.btnCreateIndex.Name = "btnCreateIndex";
this.btnCreateIndex.Size = new System.Drawing.Size(75, 23);
this.btnCreateIndex.Size = new System.Drawing.Size(100, 28);
this.btnCreateIndex.TabIndex = 0;
this.btnCreateIndex.Text = "Create Index";
this.btnCreateIndex.UseVisualStyleBackColor = true;
@ -52,17 +56,19 @@
this.txtStatus.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.txtStatus.Location = new System.Drawing.Point(13, 42);
this.txtStatus.Location = new System.Drawing.Point(17, 52);
this.txtStatus.Margin = new System.Windows.Forms.Padding(4);
this.txtStatus.Multiline = true;
this.txtStatus.Name = "txtStatus";
this.txtStatus.Size = new System.Drawing.Size(734, 507);
this.txtStatus.Size = new System.Drawing.Size(1147, 623);
this.txtStatus.TabIndex = 1;
//
// btnCreateText
//
this.btnCreateText.Location = new System.Drawing.Point(93, 12);
this.btnCreateText.Location = new System.Drawing.Point(124, 15);
this.btnCreateText.Margin = new System.Windows.Forms.Padding(4);
this.btnCreateText.Name = "btnCreateText";
this.btnCreateText.Size = new System.Drawing.Size(75, 23);
this.btnCreateText.Size = new System.Drawing.Size(100, 28);
this.btnCreateText.TabIndex = 2;
this.btnCreateText.Text = "CreateText";
this.btnCreateText.UseVisualStyleBackColor = true;
@ -70,9 +76,10 @@
//
// btnCreateStrongs
//
this.btnCreateStrongs.Location = new System.Drawing.Point(174, 12);
this.btnCreateStrongs.Location = new System.Drawing.Point(232, 15);
this.btnCreateStrongs.Margin = new System.Windows.Forms.Padding(4);
this.btnCreateStrongs.Name = "btnCreateStrongs";
this.btnCreateStrongs.Size = new System.Drawing.Size(144, 23);
this.btnCreateStrongs.Size = new System.Drawing.Size(192, 28);
this.btnCreateStrongs.TabIndex = 3;
this.btnCreateStrongs.Text = "Create Strongs Cross Refs";
this.btnCreateStrongs.UseVisualStyleBackColor = true;
@ -80,9 +87,10 @@
//
// btnCreateStrongsDict
//
this.btnCreateStrongsDict.Location = new System.Drawing.Point(324, 12);
this.btnCreateStrongsDict.Location = new System.Drawing.Point(432, 15);
this.btnCreateStrongsDict.Margin = new System.Windows.Forms.Padding(4);
this.btnCreateStrongsDict.Name = "btnCreateStrongsDict";
this.btnCreateStrongsDict.Size = new System.Drawing.Size(126, 23);
this.btnCreateStrongsDict.Size = new System.Drawing.Size(168, 28);
this.btnCreateStrongsDict.TabIndex = 4;
this.btnCreateStrongsDict.Text = "Create Strongs Dict";
this.btnCreateStrongsDict.UseVisualStyleBackColor = true;
@ -90,9 +98,10 @@
//
// btnCreateRMAC
//
this.btnCreateRMAC.Location = new System.Drawing.Point(456, 12);
this.btnCreateRMAC.Location = new System.Drawing.Point(608, 15);
this.btnCreateRMAC.Margin = new System.Windows.Forms.Padding(4);
this.btnCreateRMAC.Name = "btnCreateRMAC";
this.btnCreateRMAC.Size = new System.Drawing.Size(101, 23);
this.btnCreateRMAC.Size = new System.Drawing.Size(135, 28);
this.btnCreateRMAC.TabIndex = 5;
this.btnCreateRMAC.Text = "Create RMAC";
this.btnCreateRMAC.UseVisualStyleBackColor = true;
@ -100,19 +109,33 @@
//
// btnRmacCrossRefs
//
this.btnRmacCrossRefs.Location = new System.Drawing.Point(563, 12);
this.btnRmacCrossRefs.Location = new System.Drawing.Point(751, 15);
this.btnRmacCrossRefs.Margin = new System.Windows.Forms.Padding(4);
this.btnRmacCrossRefs.Name = "btnRmacCrossRefs";
this.btnRmacCrossRefs.Size = new System.Drawing.Size(149, 23);
this.btnRmacCrossRefs.Size = new System.Drawing.Size(199, 28);
this.btnRmacCrossRefs.TabIndex = 6;
this.btnRmacCrossRefs.Text = "Create RMAC Cross Refs";
this.btnRmacCrossRefs.UseVisualStyleBackColor = true;
this.btnRmacCrossRefs.Click += new System.EventHandler(this.btnRmacCrossRefs_Click);
//
// btnCreateGeolocationJson
//
this.btnCreateGeolocationJson.Location = new System.Drawing.Point(957, 15);
this.btnCreateGeolocationJson.Name = "btnCreateGeolocationJson";
this.btnCreateGeolocationJson.Size = new System.Drawing.Size(207, 28);
this.btnCreateGeolocationJson.TabIndex = 7;
this.btnCreateGeolocationJson.Text = "Create Geolocation JSON";
this.createGeolocationJsonTooltip.SetToolTip(this.btnCreateGeolocationJson, "Creates JSON files for geolocation data from https://www.openbible.info/geo/data/" +
"merged.txt");
this.btnCreateGeolocationJson.UseVisualStyleBackColor = true;
this.btnCreateGeolocationJson.Click += new System.EventHandler(this.btnCreateGeolocationJson_Click);
//
// frmMain
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(759, 561);
this.ClientSize = new System.Drawing.Size(1182, 690);
this.Controls.Add(this.btnCreateGeolocationJson);
this.Controls.Add(this.btnRmacCrossRefs);
this.Controls.Add(this.btnCreateRMAC);
this.Controls.Add(this.btnCreateStrongsDict);
@ -120,6 +143,7 @@
this.Controls.Add(this.btnCreateText);
this.Controls.Add(this.txtStatus);
this.Controls.Add(this.btnCreateIndex);
this.Margin = new System.Windows.Forms.Padding(4);
this.Name = "frmMain";
this.Text = "Dynamic Bible Utility";
this.ResumeLayout(false);
@ -136,6 +160,8 @@
private System.Windows.Forms.Button btnCreateStrongsDict;
private System.Windows.Forms.Button btnCreateRMAC;
private System.Windows.Forms.Button btnRmacCrossRefs;
private System.Windows.Forms.Button btnCreateGeolocationJson;
private System.Windows.Forms.ToolTip createGeolocationJsonTooltip;
}
}

View File

@ -1,4 +1,5 @@
using DynamicBible.Schemas;
using DynamicBibleUtility.Geolocation;
using JMW.Extensions.String;
using System;
using System.Collections.Generic;
@ -647,6 +648,78 @@ namespace DynamicBibleUtility
}
}
#endregion RMAC
#region Geolocation JSON
/// <summary>
/// Handles creating geolocation JSON files when the appropriate button is clicked.
/// </summary>
/// <param name="sender">Sender of the event; ignored.</param>
/// <param name="eventArguments">Event arguments; ignored.</param>
private void btnCreateGeolocationJson_Click(object sender, EventArgs eventArguments)
{
var _thread = new Thread(CreateGeolocationJson);
_thread.SetApartmentState(ApartmentState.STA);
_thread.IsBackground = true;
_thread.Start();
}
/// <summary>
/// Prompts the user for a geolocation data file (<see cref="OpenBibleDotInfoLocationParser"/>)
/// and converts a chosen file into the appropriate output JSON files for the Dynamic Bible app.
/// </summary>
private void CreateGeolocationJson()
{
// LET THE USER CHOOSE THE FILE WITH GEOLOCATION DATA.
OpenFileDialog open_file_dialog = new OpenFileDialog();
DialogResult file_dialog_result = open_file_dialog.ShowDialog();
bool file_chosen = (file_dialog_result == DialogResult.OK);
if (!file_chosen)
{
// The user chose not to create a file.
return;
}
try
{
// READ THE LOCATION INFORMATION FROM THE FILE.
IEnumerable<BibleLocationReference> locations = OpenBibleDotInfoLocationParser.Parse(open_file_dialog.FileName);
UpdateStatus($"Parsed {locations.Count()} locations.\n");
// CREATE MORE USEFUL INDICES FOR THE LOCATIONS.
// Indexing by name and verse references is useful for quick lookups
// in the Dynamic Bible app.
BibleLocationIndexByName locations_by_name = new BibleLocationIndexByName(locations);
UpdateStatus($"Finished indexing locations by name.\n");
BibleLocationIndexByVerse locations_by_verse = new BibleLocationIndexByVerse(locations);
UpdateStatus($"Finished indexing locations by verse.\n");
// WRITE OUT THE GEOLOCATION DATA TO JSON FORMAT.
const string LOCATIONS_BY_NAME_JSON_FILENAME = "locations_by_name.json";
string locations_by_name_in_json = JSON.ToJSON(locations_by_name.NameToLocationLookup);
File.WriteAllText(LOCATIONS_BY_NAME_JSON_FILENAME, locations_by_name_in_json);
UpdateStatus($"Wrote locations by name to {LOCATIONS_BY_NAME_JSON_FILENAME} in current working directory.\n");
const string LOCATIONS_BY_VERSE_JSON_FILENAME = "locations_by_verse.json";
string locations_by_verse_in_json = JSON.ToJSON(locations_by_verse.VerseToLocationNameLookup);
File.WriteAllText(LOCATIONS_BY_VERSE_JSON_FILENAME, locations_by_verse_in_json);
UpdateStatus($"Wrote locations by verse to {LOCATIONS_BY_VERSE_JSON_FILENAME} in current working directory.\n");
/// TODO(Jacob): Add Strong's numbers to the location data (with JavaScript property name of 'sn')
/// and create a lookup based on Strong's numbers. This would be more useful in the context of
/// the Dynamic Bible app.
// INFORM THE USER THAT CREATING THE GEOLOCATION JSON FILES IS COMPLETE.
UpdateStatus("Done.\n");
}
catch (Exception exception)
{
UpdateStatus($"Exception while processing geolocations: {exception}\n");
}
}
#endregion
}
}

View File

@ -117,4 +117,7 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="createGeolocationJsonTooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>