Australian Places

This dataset contains Australian places (suburbs, cities, towns etc.) that have been extracted from OpenStreetMap. The data is © OpenStreetMap contributors, please refer to their Copyright and License page for more information. The data can be downloaded here and is a simple CSV file containing name, state, latitude and longitude in decimal degrees:

Swanbourne,WA,-31.9800391,115.7650141
Swansea Heads,NSW,-33.0902878,151.6620444
Swansea,TAS,-42.124933,148.0757326
Sydenham,VIC,-37.6997223,144.7653295
Sydney,NSW,-33.8548157,151.2164539
Sylvania Waters,,-34.0185844,151.11228

The data was extracted using the C# code below that reads the OSM file as plain text and does some simple parsing to extract the items of interest. Feel free to use this code for any purposes. If you'd like to run it yourself rather than downloading the very large Planet OSM file an extract of Australia and Oceana is available from the Geofabrik GmbH OpenStreetMap Data Extracts page. The .osm.bz2 format is the necessary file.

Note that many places in the OSM data don't have the state recorded next to them and I haven't made any attempt to find missing ones by checking if they are within the bounding boxes for the states. There will be other errors and ommisions in this data so if you require something with a high degree of accuracy you might like to consider finding a different dataset.

C# Source Code


void Main()
{
	var osmFile = @"E:\Temp\australia-oceania-latest.osm";
	var csvFile = @"E:\Temp\AustraliaPlaces.csv";
	var csv = new List<string>();
	var placesOfInterest = new List<string>() {
		"borough",
		"city",
		"city block",
		"county",
		"district",
		"locaility",
		"municipality",
		"neighbourhood",
		"province",
		"suburb",
		"town",
		"village"
	};
	var stateLookup = new List<KeyValuePair<string, string>>() {
		{ new KeyValuePair<string, string>("New South Wales", "NSW")},
		{ new KeyValuePair<string, string>("Victoria", "VIC")},
		{ new KeyValuePair<string, string>("Queensland", "QLD")},
		{ new KeyValuePair<string, string>("Western Australia", "WA")},
		{ new KeyValuePair<string, string>("South Australia", "SA")},
		{ new KeyValuePair<string, string>("Tasmania", "TAS")},
		{ new KeyValuePair<string, string>("Australian Capital Territory", "ACT")},
		{ new KeyValuePair<string, string>("Northern Territory", "NT")},
		{ new KeyValuePair<string, string>("NSW,", "NSW")},
		{ new KeyValuePair<string, string>("VIC,", "VIC")},
		{ new KeyValuePair<string, string>("QLD,", "QLD")},
		{ new KeyValuePair<string, string>("WA,", "WA")},
		{ new KeyValuePair<string, string>("TAS,", "TAS")},
		{ new KeyValuePair<string, string>("Adelaide", "SA")},
		{ new KeyValuePair<string, string>("Alice Springs", "NT")},
		{ new KeyValuePair<string, string>("Brisbane", "QLD")},
		{ new KeyValuePair<string, string>("Broome", "WA")},
		{ new KeyValuePair<string, string>("Cairns", "QLD")},
		{ new KeyValuePair<string, string>("Canberra", "ACT")},
		{ new KeyValuePair<string, string>("Hobart", "TAS")},
		{ new KeyValuePair<string, string>("Kalgoorlie", "WA")},
		{ new KeyValuePair<string, string>("St Kilda", "VIC")},
		{ new KeyValuePair<string, string>("Sydney", "NSW")},
		{ new KeyValuePair<string, string>("South Austraila", "SA")}, // (sic)
	};
	bool insideNode = false;
	var tags = new Dictionary<string, string>();
	double? lastLat = null, lastLon = null;
	foreach (var line in File.ReadLines(osmFile))
	{
		if (line.StartsWith("  <node") && !line.EndsWith("/>"))
		{
			insideNode = true;
			tags.Clear();
			lastLat = ExtractDouble(line, "lat");
			if (lastLat == null)
			{
				lastLon = null;
			}
			else
			{
				lastLon = ExtractDouble(line, "lon");
			}
		}
		else if (line.StartsWith("  </node>"))
		{
			if (tags.ContainsKey("place") && placesOfInterest.Contains(tags["place"])
				&& (tags.ContainsKey("is_in") && tags["is_in"].Contains("Australia")
					|| tags.ContainsKey("is_in:country") && tags["is_in:country"].Contains("Australia")
					|| tags.ContainsKey("wikipedia") && tags["wikipedia"].Contains("Australia")
					|| tags.ContainsKey("ref:nswgnb")
				)
				&& tags.ContainsKey("name")
				&& lastLat != null
				&& lastLon != null)
			{
				string stateCode = "";
				if (tags.ContainsKey("is_in:state_code"))
				{
					stateCode = tags["is_in:state_code"];
				}
				else
				{
					if (tags.ContainsKey("is_in:state"))
					{
						stateCode = tags["is_in:state"];
					}
					else if (tags.ContainsKey("is_in"))
					{
						stateCode = tags["is_in"];
					}
					else if (tags.ContainsKey("wikipedia"))
					{
						stateCode = tags["wikipedia"];
					}
				}
				if (stateCode.Length > 3 || stateCode.Length == 0)
				{
					var stateCodeLookup = stateLookup
						.Where(sl => stateCode.IndexOf(sl.Key, StringComparison.OrdinalIgnoreCase) >= 0)
						.Select(sl => sl.Value)
						.FirstOrDefault();
					if (stateCodeLookup == null)
					{
						stateCodeLookup = stateLookup
							.Where(sl => tags["name"].IndexOf(sl.Key, StringComparison.OrdinalIgnoreCase) >= 0)
							.Select(sl => sl.Value)
							.FirstOrDefault();
					}
					stateCode = stateCodeLookup ?? "";
				}
				csv.Add($"{tags["name"]},{stateCode},{lastLat},{lastLon}");
			}
			insideNode = false;
		}
		else if (insideNode && line.StartsWith("    <tag"))
		{
			string key = ExtractString(line, "k");
			string value = ExtractString(line, "v");
			tags[key] = value;
		}
	}
	csv.Sort((a, b) => a.CompareTo(b));
	File.WriteAllLines(csvFile, csv);
}


private string ExtractString(string s, string attribute)
{
	string result = null;
	string prefix = attribute + "=\"";
	int prefixPos = s.IndexOf(prefix);
	if (prefixPos >= 0)
	{
		prefixPos += prefix.Length;
		result = "";
		while (prefixPos < s.Length && s[prefixPos] != '"')
		{
			result += s[prefixPos++];
		}
	}
	return result;
}

private double? ExtractDouble(string s, string attribute)
{
	double? result = null;
	string value = ExtractString(s, attribute);
	if (!String.IsNullOrEmpty(value) && Double.TryParse(value, out double temp))
	{
		result = temp;
	}
	return result;
}