mirror of
https://gitlab.com/walljm/dynamicbible.git
synced 2025-07-25 00:09:54 -04:00
DynamicBibleUtility - Adding initial geolocation JSON creation.
Still need to incorporate Strong's numbers.
This commit is contained in:
parent
8fb3388392
commit
b67e52950a
@ -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" />
|
||||
|
177
DynamicBibleUtility/DynamicBibleUtility/Geolocation/BibleBook.cs
Normal file
177
DynamicBibleUtility/DynamicBibleUtility/Geolocation/BibleBook.cs
Normal 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];
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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>
|
Loading…
x
Reference in New Issue
Block a user