<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[TimeToTime Tools]]></title><description><![CDATA[TimeToTime Tools]]></description><link>https://timetotime-tools.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>TimeToTime Tools</title><link>https://timetotime-tools.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 13 Oct 2026 14:34:00 GMT</lastBuildDate><atom:link href="https://timetotime-tools.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Six checks that catch KML-to-CSV data loss before Excel does]]></title><description><![CDATA[A CSV can open cleanly and still misrepresent the KML it came from. Coordinates may be swapped, a missing altitude may become 0, multiple points inside one placemark may collapse to one row, or an ide]]></description><link>https://timetotime-tools.hashnode.dev/six-checks-that-catch-kml-to-csv-data-loss-before-excel-does</link><guid isPermaLink="true">https://timetotime-tools.hashnode.dev/six-checks-that-catch-kml-to-csv-data-loss-before-excel-does</guid><category><![CDATA[Python]]></category><category><![CDATA[GIS]]></category><category><![CDATA[csv]]></category><dc:creator><![CDATA[TimeToTime Tools]]></dc:creator><pubDate>Sat, 03 Oct 2026 19:59:46 GMT</pubDate><content:encoded><![CDATA[<p>A CSV can open cleanly and still misrepresent the KML it came from. Coordinates may be swapped, a missing altitude may become <code>0</code>, multiple points inside one placemark may collapse to one row, or an identifier such as <code>001</code> may become <code>1</code> when a spreadsheet guesses its type. A row-count check alone misses most of that.</p>
<p>Here is a small validation method for <strong>point-table exports</strong>. It does not test whether a CSV reproduces a complete map. Google's <a href="https://developers.google.com/kml/documentation/kmlreference">KML reference</a> distinguishes <code>Point</code>, <code>LineString</code> and other geometries; a tool that exports point rows may intentionally omit a route or polygon.</p>
<h2>Define the expected result before exporting</h2>
<p>Use two tiny, fictional KML documents:</p>
<table>
<thead>
<tr>
<th>Source</th>
<th>Expected point-table result</th>
<th>What else to inspect</th>
</tr>
</thead>
<tbody><tr>
<td>Two separate point placemarks</td>
<td>Two rows. The first has longitude <code>-0.125700</code>, latitude <code>51.508500</code>, name <code>Café, "North"</code>, code <code>001</code> and a description with a newline. The second has no altitude, code <code>002</code> and a formula-like Note <code>=1+1</code>.</td>
<td>Check whether the exporter protects formula-like spreadsheet text, and keep IDs as text when importing.</td>
</tr>
<tr>
<td>One placemark with two Points plus a separate LineString</td>
<td>Two rows, with Point numbers <code>1</code> and <code>2</code>. The first altitude is the explicit value <code>0</code>; the second altitude is empty.</td>
<td>Confirm the converter reports that the LineString was excluded. A CSV by itself cannot prove that warning appeared.</td>
</tr>
</tbody></table>
<p>KML coordinates are <strong>longitude, latitude, optional altitude</strong>. The exact decimal strings are useful test values because a swap or premature rounding is obvious. If your workflow needs the omitted line, a polygon, styling or coordinate reprojection, use a GIS export that handles those features and keep the original map.</p>
<h2>Run six assertion groups</h2>
<p>Save the two exported files as <code>points.csv</code> and <code>mixed.csv</code>. This Python script expects the column names and protective Note behavior shown in the table; adapt those parts if your exporter uses a different schema. Reading with <code>utf-8-sig</code> handles a UTF-8 BOM if one is present, while <code>newline=""</code> lets Python's CSV parser handle quoted multiline fields.</p>
<pre><code class="language-python">import csv


def read_rows(path):
    with open(path, encoding="utf-8-sig", newline="") as stream:
        return list(csv.DictReader(stream))


points = read_rows("points.csv")
mixed = read_rows("mixed.csv")

# 1. Did the ordinary point document yield both locations?
assert len(points) == 2

# 2. Did coordinate order and source precision survive?
assert (points[0]["Longitude"], points[0]["Latitude"]) == (
    "-0.125700", "51.508500"
)

# 3. Is explicit zero different from missing altitude?
assert (mixed[0]["Altitude"], mixed[1]["Altitude"]) == ("0", "")

# 4. Did both Points inside one placemark become separate rows?
assert [(row["Placemark ID"], row["Point number"]) for row in mixed] == [
    ("pair-003", "1"), ("pair-003", "2")
]

# 5. Did quoting and Unicode survive a CSV round-trip?
assert points[0]["Name"] == 'Café, "North"'
assert "\n" in points[0]["Description"]

# 6. Did text identifiers survive, and what did text protection do?
assert [row["Data: Site code"] for row in points] == ["001", "002"]
assert points[1]["Data: Note"] == "'=1+1"
</code></pre>
<p>That last Note assertion is <strong>specific to an export made with formula-like text protection enabled</strong>. Other exporters may escape the cell differently or leave it unprotected; set the expected value to match your intended safety policy. <a href="https://community.owasp.org/attacks/CSV_Injection">OWASP's CSV injection guidance</a> explains why text beginning like a spreadsheet formula needs deliberate treatment. Do not assume any CSV escaping choice is safe in every spreadsheet and workflow.</p>
<p>The mixed document also contains one LineString. Check the source and the converter's exclusion or partial-export message before accepting the two-row CSV. If the message is absent, the script cannot tell whether the line was intentionally excluded or silently lost. Treat that as an unresolved conversion result, not a successful full-map export.</p>
<p>Finally, import the CSV into Excel with the Site code column explicitly set to <strong>Text</strong>. CSV does not specify column types, and Excel may strip leading zeros when it interprets <code>001</code> as a number. <a href="https://support.microsoft.com/en-us/excel/keeping-leading-zeros-and-large-numbers">Microsoft's import guidance</a> describes the Text/CSV import path and the text-type choice. Validate the file <em>before</em> spreadsheet import, then compare what the spreadsheet displays.</p>
<p><strong>Optional reproducible files:</strong> I work on TimeToTime, which publishes the <a href="https://timetotime.net/resources/kml-csv/v1/kml-csv-reference-pack.zip">synthetic KML and expected-CSV fixture pack</a> used for these checks. It includes both KML inputs, expected CSVs and reuse instructions. You can copy or adapt the original sample files without attribution or a backlink. The method above also works with your own fixtures and exporter once you specify its intended columns and exclusions.</p>
<p><em>Disclosure: This article was prepared with AI assistance. Its assertions were run against the synthetic files in the project repository.</em></p>
]]></content:encoded></item></channel></rss>