added datasim and kmz tools

This commit is contained in:
Nico Melone
2026-02-04 18:00:22 -06:00
parent 78b0a7c2bf
commit 076970a20c
15 changed files with 908 additions and 0 deletions

133
Code Snippets/dataSim.py Normal file
View File

@@ -0,0 +1,133 @@
#!/usr/bin/env python3
"""
mqtt_counter_publish.py
-----------------------
* Uses `paho.mqtt.client` to connect to a broker.
* Publishes a list of *static* payloads to a topic.
* Measures bytes sent/received by the **process** with `psutil`.
* Prints a neat summary at the end.
"""
import os
import json
import time
import random
import psutil
import paho.mqtt.client as mqtt
# ------------------------------------------------------------------
# Helper: return write_bytes / read_bytes of the current process
# ------------------------------------------------------------------
def get_io_counters():
"""Return (write_bytes, read_bytes) for the current process."""
proc = psutil.Process(os.getpid())
io = proc.io_counters() # .write_bytes, .read_bytes
return io.write_bytes, io.read_bytes #io.write_chars, io.read_chars #io.write_bytes, io.read_bytes
def print_io_delta(start, end):
"""Print the difference between two (write, read) tuples."""
sent = end[0] - start[0]
recv = end[1] - start[1]
print(f"\nΔ sent: {sent:,} bytes")
print(f"Δ received: {recv:,} bytes\n")
# ------------------------------------------------------------------
# MQTT callbacks (minimal we don't care about incoming messages)
# ------------------------------------------------------------------
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("✅ Connected to broker")
else:
print(f"❌ Connect failed (rc={rc})")
# ------------------------------------------------------------------
# Publish static payloads
# ------------------------------------------------------------------
def publish_static_payloads(client, topic, payloads, delay=0.1):
"""
Publish each payload in `payloads` to `topic`.
`delay` (seconds) can be used to throttle the rate.
"""
for i, payload in enumerate(payloads, 1):
result = client.publish(topic, json.dumps(payload))
# Wait for the network loop to actually send it
client.loop(timeout=0.1)
if result.rc != mqtt.MQTT_ERR_SUCCESS:
print(f"⚠️ Publish {i} failed with rc={result.rc}")
else:
print(f"→ Sent msg #{i} ({len(json.dumps(payload))} bytes)")
time.sleep(delay)
# ------------------------------------------------------------------
# Main routine
# ------------------------------------------------------------------
def main():
broker = "hp.henrypump.cloud" # change if needed
port = 1883 # default MQTT
topic = "v1/devices/me/telemetry"
# Static payloads feel free to replace with your own data
payloads = []
now = time.time() * 1000
for x in range(25):
values = {}
for y in range(20):
values[f"my_telemetry_{x+y}"] = random.random()
payloads.append({"ts": now, "values": values})
payloads = [{"test1k": "A" * 1024}]
# ------------------------------------------------------------------
# Create MQTT client, connect
# ------------------------------------------------------------------
client = mqtt.Client()
client.username_pw_set("e5xv3wfi1oa44ty2ydv2")
client.on_connect = on_connect
client.connect(broker, port, keepalive=60)
# -------------------------------------------------------------
# 1⃣ Capture baseline I/O counters **before** any traffic
# -------------------------------------------------------------
baseline = get_io_counters()
print("\n📊 Baseline I/O counters:")
print(f" Written: {baseline[0]:,} bytes")
print(f" Read: {baseline[1]:,} bytes")
# -------------------------------------------------------------
# 2⃣ Start network loop in a background thread
# -------------------------------------------------------------
client.loop_start()
# -------------------------------------------------------------
# 3⃣ Publish the static payloads
# -------------------------------------------------------------
print("\n📤 Publishing static payloads …")
for _ in range(1):
publish_static_payloads(client, topic, payloads, delay=0.2)
# Give the broker a moment to ack / reply (if any)
time.sleep(10)
# -------------------------------------------------------------
# 4⃣ Capture I/O counters **after** traffic
# -------------------------------------------------------------
final = get_io_counters()
print("\n📊 Final I/O counters:")
print(f" Written: {final[0]:,} bytes")
print(f" Read: {final[1]:,} bytes")
# -------------------------------------------------------------
# 5⃣ Report the delta
# -------------------------------------------------------------
print_io_delta(baseline, final)
# -------------------------------------------------------------
# Cleanup
# -------------------------------------------------------------
client.loop_stop()
client.disconnect()
if __name__ == "__main__":
main()

Binary file not shown.

View File

@@ -0,0 +1,153 @@
from scapy.all import *
import psutil
from collections import defaultdict
import os, time
from datetime import datetime
from threading import Thread
import pandas as pd
# get the all network adapter's MAC addresses
all_macs = {iface.mac for iface in ifaces.values()}
# A dictionary to map each connection to its correponding process ID (PID)
connection2pid = {}
# A dictionary to map each process ID (PID) to total Upload (0) and Download (1) traffic
pid2traffic = defaultdict(lambda: [0, 0])
# the global Pandas DataFrame that's used to track previous traffic stats
global_df = None
# global boolean for status of the program
is_program_running = True
def get_size(bytes):
"""
Returns size of bytes in a nice format
"""
for unit in ['', 'K', 'M', 'G', 'T', 'P']:
if bytes < 1024:
return f"{bytes:.2f}{unit}B"
bytes /= 1024
def process_packet(packet):
global pid2traffic
try:
# get the packet source & destination IP addresses and ports
packet_connection = (packet.sport, packet.dport)
except (AttributeError, IndexError):
# sometimes the packet does not have TCP/UDP layers, we just ignore these packets
pass
else:
# get the PID responsible for this connection from our `connection2pid` global dictionary
packet_pid = connection2pid.get(packet_connection)
if packet_pid:
if packet.src in all_macs:
# the source MAC address of the packet is our MAC address
# so it's an outgoing packet, meaning it's upload
pid2traffic[packet_pid][0] += len(packet)
else:
# incoming packet, download
pid2traffic[packet_pid][1] += len(packet)
def get_connections():
"""A function that keeps listening for connections on this machine
and adds them to `connection2pid` global variable"""
global connection2pid
while is_program_running:
# using psutil, we can grab each connection's source and destination ports
# and their process ID
for c in psutil.net_connections():
if c.laddr and c.raddr and c.pid:
# if local address, remote address and PID are in the connection
# add them to our global dictionary
connection2pid[(c.laddr.port, c.raddr.port)] = c.pid
connection2pid[(c.raddr.port, c.laddr.port)] = c.pid
# sleep for a second, feel free to adjust this
time.sleep(1)
def print_pid2traffic():
global global_df
# initialize the list of processes
processes = []
for pid, traffic in pid2traffic.items():
# `pid` is an integer that represents the process ID
# `traffic` is a list of two values: total Upload and Download size in bytes
try:
# get the process object from psutil
p = psutil.Process(pid)
except psutil.NoSuchProcess:
# if process is not found, simply continue to the next PID for now
continue
# get the name of the process, such as chrome.exe, etc.
name = p.name()
# get the time the process was spawned
try:
create_time = datetime.fromtimestamp(p.create_time())
except OSError:
# system processes, using boot time instead
create_time = datetime.fromtimestamp(psutil.boot_time())
# construct our dictionary that stores process info
process = {
"pid": pid, "name": name, "create_time": create_time, "Upload": traffic[0],
"Download": traffic[1],
}
try:
# calculate the upload and download speeds by simply subtracting the old stats from the new stats
process["Upload Speed"] = traffic[0] - global_df.at[pid, "Upload"]
process["Download Speed"] = traffic[1] - global_df.at[pid, "Download"]
except (KeyError, AttributeError):
# If it's the first time running this function, then the speed is the current traffic
# You can think of it as if old traffic is 0
process["Upload Speed"] = traffic[0]
process["Download Speed"] = traffic[1]
# append the process to our processes list
processes.append(process)
# construct our Pandas DataFrame
df = pd.DataFrame(processes)
try:
# set the PID as the index of the dataframe
df = df.set_index("pid")
# sort by column, feel free to edit this column
df.sort_values("Download", inplace=True, ascending=False)
except KeyError as e:
# when dataframe is empty
pass
# make another copy of the dataframe just for fancy printing
printing_df = df.copy()
try:
# apply the function get_size to scale the stats like '532.6KB/s', etc.
printing_df["Download"] = printing_df["Download"].apply(get_size)
printing_df["Upload"] = printing_df["Upload"].apply(get_size)
printing_df["Download Speed"] = printing_df["Download Speed"].apply(get_size).apply(lambda s: f"{s}/s")
printing_df["Upload Speed"] = printing_df["Upload Speed"].apply(get_size).apply(lambda s: f"{s}/s")
except KeyError as e:
# when dataframe is empty again
pass
# clear the screen based on your OS
os.system("cls") if "nt" in os.name else os.system("clear")
# print our dataframe
print(printing_df.to_string())
# update the global df to our dataframe
global_df = df
def print_stats():
"""Simple function that keeps printing the stats"""
while is_program_running:
time.sleep(1)
print_pid2traffic()
if __name__ == "__main__":
# start the printing thread
printing_thread = Thread(target=print_stats)
printing_thread.start()
# start the get_connections() function to update the current connections of this machine
connections_thread = Thread(target=get_connections)
connections_thread.start()
# start sniffing
print("Started sniffing")
sniff(prn=process_packet, store=False)
# setting the global variable to False to exit the program
is_program_running = False

View File

@@ -0,0 +1,142 @@
#!/usr/bin/env python3
"""
KML coordinate extractor
------------------------
* Every <Polygon> element → list of [lat, lon] pairs, written to *output_polygons*.
* Every <LineString> element → list of [lat, lon] pairs, written to *output_linestrings*.
* Every <Placemark> that contains a <name> and a <Point> → “Marker Name: lat, lon”
written to *output_markers*.
The coordinates in a KML file are stored as `lon,lat[,alt]`.
The script converts them to `lat,lon` because thats the format you asked for.
"""
import xml.etree.ElementTree as ET
import sys
from pathlib import Path
# ----------------------------------------------------------------------
# Helpers
# ----------------------------------------------------------------------
def parse_kml(file_path: Path) -> ET.ElementTree:
"""Parse the KML file; exit with a message on failure."""
try:
return ET.parse(file_path)
except ET.ParseError as exc:
print(f"ERROR: Failed to parse {file_path}: {exc}", file=sys.stderr)
sys.exit(1)
def _lon_lat_to_list(coord_str: str) -> list[list[float]]:
"""
Convert a KML coordinate string (lon,lat[,alt] …) into a list of [lat, lon].
Ignores malformed entries.
"""
coords = []
for pair in coord_str.strip().split():
parts = pair.split(',')
if len(parts) < 2:
continue
lon, lat = float(parts[0]), float(parts[1])
coords.append([lat, lon])
return coords
# ----------------------------------------------------------------------
# Main extraction routine
# ----------------------------------------------------------------------
def extract_kml(
kml_file: Path,
out_polygons: Path,
out_linestrings: Path,
out_markers: Path,
) -> None:
tree = parse_kml(kml_file)
root = tree.getroot()
# Namespace handling (most KML files use the same namespace)
ns = {'kml': root.tag.split('}')[0].strip('{')}
# ------------------------------------------------------------------
# 1) Polygons
# ------------------------------------------------------------------
polygons = [] # List[ List[ [lat, lon] ] ]
for poly in root.findall('.//kml:Polygon', ns):
coord_el = poly.find('.//kml:coordinates', ns)
if coord_el is None or not coord_el.text:
continue
polygons.append(_lon_lat_to_list(coord_el.text))
# Write polygons
with out_polygons.open('w', encoding='utf-8') as fp:
for i, poly in enumerate(polygons, start=1):
fp.write(f"Polygon {i}:\n")
fp.write(str(poly) + "\n\n")
# ------------------------------------------------------------------
# 2) LineStrings
# ------------------------------------------------------------------
linestrings = [] # List[ List[ [lat, lon] ] ]
for ls in root.findall('.//kml:LineString', ns):
coord_el = ls.find('.//kml:coordinates', ns)
if coord_el is None or not coord_el.text:
continue
linestrings.append(_lon_lat_to_list(coord_el.text))
# Write LineStrings
with out_linestrings.open('w', encoding='utf-8') as fp:
for i, ls in enumerate(linestrings, start=1):
fp.write(f"LineString {i}:\n")
fp.write(str(ls) + "\n\n")
# ------------------------------------------------------------------
# 3) Markers (Point placemarks with a name)
# ------------------------------------------------------------------
markers = [] # List[ (name, [lat, lon]) ]
for pm in root.findall('.//kml:Placemark', ns):
name_el = pm.find('kml:name', ns)
point_el = pm.find('.//kml:Point/kml:coordinates', ns)
if name_el is None or point_el is None:
continue
name = name_el.text.strip()
coord_list = _lon_lat_to_list(point_el.text)
if not coord_list:
continue
markers.append((name, coord_list[0])) # a point has exactly one pair
# Write markers
with out_markers.open('w', encoding='utf-8') as fp:
for name, (lat, lon) in markers:
fp.write(f"{name}: {lat}, {lon}\n")
# ------------------------------------------------------------------
# Summary
# ------------------------------------------------------------------
print(f"Polygons written to: {out_polygons}")
print(f"LineStrings written to: {out_linestrings}")
print(f"Markers written to: {out_markers}")
# ----------------------------------------------------------------------
# Entrypoint
# ----------------------------------------------------------------------
def main() -> None:
if len(sys.argv) != 5:
print(
"Usage: python breakout_kml.py <input_file> "
"<output_polygons> <output_linestrings> <output_markers>"
)
sys.exit(1)
input_file = Path(sys.argv[1]).expanduser().resolve()
out_polygons = Path(sys.argv[2]).expanduser().resolve()
out_linestrings = Path(sys.argv[3]).expanduser().resolve()
out_markers = Path(sys.argv[4]).expanduser().resolve()
if not input_file.exists():
print(f"ERROR: Input file not found: {input_file}", file=sys.stderr)
sys.exit(1)
extract_kml(input_file, out_polygons, out_linestrings, out_markers)
if __name__ == "__main__":
main()

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,120 @@
#!/usr/bin/env python3
"""
combine_kmz_to_kml.py
Combine all KMZ files in a folder into a single KML file.
Usage:
python combine_kmz_to_kml.py <input_folder> <output_kml>
Example:
python combine_kmz_to_kml.py ./kmz_files all_points.kml
"""
import os
import sys
import zipfile
import xml.etree.ElementTree as ET
from pathlib import Path
from typing import List
# ------------------------------------------------------------
# Helper functions
# ------------------------------------------------------------
def find_kmz_files(folder: Path) -> List[Path]:
"""Return a list of .kmz files in the given folder (nonrecursive)."""
return list(folder.rglob("*.kmz"))
def extract_kml_from_kmz(kmz_path: Path) -> ET.ElementTree:
"""
Extract the first KML file found inside a KMZ archive.
Returns an ElementTree of the extracted KML.
"""
with zipfile.ZipFile(kmz_path, "r") as z:
# Find the first file ending with .kml (caseinsensitive)
kml_name = next((n for n in z.namelist()
if n.lower().endswith(".kml")), None)
if not kml_name:
raise ValueError(f"No KML file found inside {kmz_path}")
with z.open(kml_name) as kml_file:
return ET.parse(kml_file)
def get_top_level_elements(kml_tree: ET.ElementTree) -> List[ET.Element]:
"""
Return a list of the toplevel elements that contain placemarks.
Common patterns are:
<Document>
<Folder>
<Placemark>
"""
root = kml_tree.getroot()
# KML namespace handling (most KMLs use the same namespace)
ns = {"kml": "http://www.opengis.net/kml/2.2"}
# Find <Document> or <Folder> or <Placemark> elements directly under root
elements = []
for tag in ["Document", "Folder", "Placemark"]:
elements.extend(root.findall(f"kml:{tag}", ns))
return elements
def build_merged_kml(elements_by_file: List[List[ET.Element]]) -> ET.ElementTree:
"""
Build a new KML ElementTree that contains a single <Document>
wrapping all collected elements.
"""
kml_ns = "http://www.opengis.net/kml/2.2"
ET.register_namespace("", kml_ns) # ensure no prefix in output
kml_root = ET.Element(f"{{{kml_ns}}}kml")
doc = ET.SubElement(kml_root, "Document")
for elems in elements_by_file:
for elem in elems:
doc.append(elem)
return ET.ElementTree(kml_root)
# ------------------------------------------------------------
# Main logic
# ------------------------------------------------------------
def main(input_folder: Path, output_file: Path):
kmz_files = find_kmz_files(input_folder)
if not kmz_files:
print(f"No .kmz files found in {input_folder}")
sys.exit(1)
all_elements = []
for kmz in kmz_files:
try:
kml_tree = extract_kml_from_kmz(kmz)
elems = get_top_level_elements(kml_tree)
if not elems:
print(f"WARNING: No placemark elements found in {kmz}")
else:
all_elements.append(elems)
except Exception as e:
print(f"ERROR processing {kmz}: {e}")
if not any(all_elements):
print("No placemark data to write.")
sys.exit(1)
merged_tree = build_merged_kml(all_elements)
merged_tree.write(output_file, encoding="utf-8", xml_declaration=True)
print(f"Combined KML written to {output_file}")
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: python combine_kmz_to_kml.py <input_folder> <output_kml>")
sys.exit(1)
input_dir = Path(sys.argv[1]).expanduser().resolve()
output_kml = Path(sys.argv[2]).expanduser().resolve()
if not input_dir.is_dir():
print(f"Input path {input_dir} is not a directory.")
sys.exit(1)
main(input_dir, output_kml)

View File

@@ -0,0 +1,3 @@
LineString 1:
[[31.65053381256015, -103.1381372489133], [31.65078110147886, -103.1371939568141], [31.65137766837301, -103.135379572764], [31.65501734857683, -103.1216582043267], [31.66820232963991, -103.1267090001346], [31.66857428692562, -103.1254217441416], [31.68730649748832, -103.097869762411], [31.69401658057209, -103.0880082019376], [31.71262120882616, -103.0201699677156], [31.71246042343891, -103.0199628293717], [31.71229701578655, -103.0197573453124], [31.71245394748738, -103.0195184188874], [31.63091832800928, -102.9041726111906], [31.6165910414367, -102.8944523887092], [31.59718777074555, -102.8811494904473], [31.58231202839571, -102.8680137394599], [31.56931339800882, -102.8516886598823], [31.48448961907497, -102.7180735327187], [31.51503752317085, -102.7283930769256], [31.51898260340799, -102.7117114856601], [31.5330847136263, -102.7165257753119], [31.53686069788628, -102.7003948024066], [31.56540947523578, -102.7095293371793], [31.58496921843023, -102.6901307183483], [31.60301750051444, -102.6955769601148]]

View File

@@ -0,0 +1,5 @@
Node X: Cipher Interconnect: 31.69772151150734, -103.0755810899755
Node D: 10" to 12" Transition - Wadell Valve: 31.48528462529936, -102.7191228273435
Node F: US Silica: 31.60330030384848, -102.6955825380853
RRIG Water Pump Station: 31.65239911273985, -103.1419105981684
Node C: Monahans Booster Station: 31.59192479909473, -102.8767681214162

View File

@@ -0,0 +1,352 @@
<?xml version='1.0' encoding='utf-8'?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:ns1="http://www.google.com/kml/ext/2.2" xmlns:ns2="http://www.w3.org/2005/Atom"><Document><Document>
<name>Node X Cipher Interconnect.kmz</name>
<StyleMap id="msn_X">
<Pair>
<key>normal</key>
<styleUrl>#sn_X</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#sh_X</styleUrl>
</Pair>
</StyleMap>
<Style id="sh_X">
<IconStyle>
<scale>1.3</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/paddle/X.png</href>
</Icon>
<hotSpot x="32" y="1" xunits="pixels" yunits="pixels" />
</IconStyle>
<ListStyle>
<ItemIcon>
<href>http://maps.google.com/mapfiles/kml/paddle/X-lv.png</href>
</ItemIcon>
</ListStyle>
</Style>
<Style id="sn_X">
<IconStyle>
<scale>1.1</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/paddle/X.png</href>
</Icon>
<hotSpot x="32" y="1" xunits="pixels" yunits="pixels" />
</IconStyle>
<ListStyle>
<ItemIcon>
<href>http://maps.google.com/mapfiles/kml/paddle/X-lv.png</href>
</ItemIcon>
</ListStyle>
</Style>
<Placemark>
<name>Node X: Cipher Interconnect</name>
<LookAt>
<longitude>-103.0664786491922</longitude>
<latitude>31.67995227525221</latitude>
<altitude>0</altitude>
<heading>-20.19425301511447</heading>
<tilt>0.3113470562703879</tilt>
<range>19074.24725536319</range>
<ns1:altitudeMode>relativeToSeaFloor</ns1:altitudeMode>
</LookAt>
<styleUrl>#msn_X</styleUrl>
<Point>
<ns1:drawOrder>1</ns1:drawOrder>
<coordinates>-103.0755810899755,31.69772151150734,0</coordinates>
</Point>
<ns2:link rel="app" href="https://www.google.com/earth/about/versions/#earth-pro" title="Google Earth Pro 7.3.6.10441" />
</Placemark>
</Document>
<Document>
<name>Node D 10 to 12 Transition - Wadell Valve.kmz</name>
<StyleMap id="msn_D">
<Pair>
<key>normal</key>
<styleUrl>#sn_D</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#sh_D</styleUrl>
</Pair>
</StyleMap>
<Style id="sh_D">
<IconStyle>
<scale>1.3</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/paddle/D.png</href>
</Icon>
<hotSpot x="32" y="1" xunits="pixels" yunits="pixels" />
</IconStyle>
<ListStyle>
<ItemIcon>
<href>http://maps.google.com/mapfiles/kml/paddle/D-lv.png</href>
</ItemIcon>
</ListStyle>
</Style>
<Style id="sn_D">
<IconStyle>
<scale>1.1</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/paddle/D.png</href>
</Icon>
<hotSpot x="32" y="1" xunits="pixels" yunits="pixels" />
</IconStyle>
<ListStyle>
<ItemIcon>
<href>http://maps.google.com/mapfiles/kml/paddle/D-lv.png</href>
</ItemIcon>
</ListStyle>
</Style>
<Placemark>
<name>Node D: 10" to 12" Transition - Wadell Valve</name>
<description>This is a proposed booster station location.
Before this proposed booster station is NEW 10" CS SCH/40 and after the pump it transitions to the existing 12" CS SCH/STD.</description>
<LookAt>
<longitude>-102.714901153744</longitude>
<latitude>31.49028973104173</latitude>
<altitude>0</altitude>
<heading>0.2075710380470465</heading>
<tilt>34.53924441923252</tilt>
<range>3707.334765680453</range>
<ns1:altitudeMode>relativeToSeaFloor</ns1:altitudeMode>
</LookAt>
<styleUrl>#msn_D</styleUrl>
<Point>
<ns1:drawOrder>1</ns1:drawOrder>
<coordinates>-102.7191228273435,31.48528462529936,0</coordinates>
</Point>
<ns2:link rel="app" href="https://www.google.com/earth/about/versions/#earth-pro" title="Google Earth Pro 7.3.6.10441" />
</Placemark>
</Document>
<Document>
<name>Node F US Silica.kmz</name>
<StyleMap id="msn_F">
<Pair>
<key>normal</key>
<styleUrl>#sn_F</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#sh_F</styleUrl>
</Pair>
</StyleMap>
<Style id="sh_F">
<IconStyle>
<scale>1.3</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/paddle/F.png</href>
</Icon>
<hotSpot x="32" y="1" xunits="pixels" yunits="pixels" />
</IconStyle>
<ListStyle>
<ItemIcon>
<href>http://maps.google.com/mapfiles/kml/paddle/F-lv.png</href>
</ItemIcon>
</ListStyle>
</Style>
<Style id="sn_F">
<IconStyle>
<scale>1.1</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/paddle/F.png</href>
</Icon>
<hotSpot x="32" y="1" xunits="pixels" yunits="pixels" />
</IconStyle>
<ListStyle>
<ItemIcon>
<href>http://maps.google.com/mapfiles/kml/paddle/F-lv.png</href>
</ItemIcon>
</ListStyle>
</Style>
<Placemark>
<name>Node F: US Silica</name>
<description>This is the final destination (US Silica).</description>
<LookAt>
<longitude>-102.7057483945038</longitude>
<latitude>31.58085164971466</latitude>
<altitude>0</altitude>
<heading>0.1391520682125403</heading>
<tilt>14.07815682751618</tilt>
<range>9472.365324672779</range>
<ns1:altitudeMode>relativeToSeaFloor</ns1:altitudeMode>
</LookAt>
<styleUrl>#msn_F</styleUrl>
<ns1:balloonVisibility>1</ns1:balloonVisibility>
<Point>
<ns1:drawOrder>1</ns1:drawOrder>
<coordinates>-102.6955825380853,31.60330030384848,0</coordinates>
</Point>
<ns2:link rel="app" href="https://www.google.com/earth/about/versions/#earth-pro" title="Google Earth Pro 7.3.6.10441" />
</Placemark>
</Document>
<Document>
<name>RRIG Water Pump Station.kmz</name>
<StyleMap id="msn_ylw-pushpin">
<Pair>
<key>normal</key>
<styleUrl>#sn_ylw-pushpin</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#sh_ylw-pushpin</styleUrl>
</Pair>
</StyleMap>
<Style id="sh_ylw-pushpin">
<IconStyle>
<scale>1.3</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels" />
</IconStyle>
<BalloonStyle>
</BalloonStyle>
</Style>
<Style id="sn_ylw-pushpin">
<IconStyle>
<scale>1.1</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels" />
</IconStyle>
<BalloonStyle>
</BalloonStyle>
</Style>
<Placemark>
<name>RRIG Water Pump Station</name>
<LookAt>
<longitude>-103.1424336640816</longitude>
<latitude>31.65637246137589</latitude>
<altitude>0</altitude>
<heading>-0.01350029730544429</heading>
<tilt>33.60918004711282</tilt>
<range>3855.687513264544</range>
<ns1:altitudeMode>relativeToSeaFloor</ns1:altitudeMode>
</LookAt>
<styleUrl>#msn_ylw-pushpin</styleUrl>
<Point>
<ns1:drawOrder>1</ns1:drawOrder>
<coordinates>-103.1419105981684,31.65239911273985,0</coordinates>
</Point>
<ns2:link rel="app" href="https://www.google.com/earth/about/versions/#earth-pro" title="Google Earth Pro 7.3.6.10441" />
</Placemark>
</Document>
<Document>
<name>Node C Monahans Booster Station.kmz</name>
<StyleMap id="msn_C">
<Pair>
<key>normal</key>
<styleUrl>#sn_C</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#sh_C</styleUrl>
</Pair>
</StyleMap>
<Style id="sh_C">
<IconStyle>
<scale>1.3</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/paddle/C.png</href>
</Icon>
<hotSpot x="32" y="1" xunits="pixels" yunits="pixels" />
</IconStyle>
<BalloonStyle>
</BalloonStyle>
<ListStyle>
<ItemIcon>
<href>http://maps.google.com/mapfiles/kml/paddle/C-lv.png</href>
</ItemIcon>
</ListStyle>
</Style>
<Style id="sn_C">
<IconStyle>
<scale>1.1</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/paddle/C.png</href>
</Icon>
<hotSpot x="32" y="1" xunits="pixels" yunits="pixels" />
</IconStyle>
<BalloonStyle>
</BalloonStyle>
<ListStyle>
<ItemIcon>
<href>http://maps.google.com/mapfiles/kml/paddle/C-lv.png</href>
</ItemIcon>
</ListStyle>
</Style>
<Placemark>
<name>Node C: Monahans Booster Station</name>
<description>Here is an exisiting booster station. The pumps here will require an upgrade/new pumps.
Before the booster station is 16" SDR-11 HDPE and after the pump it transitions to the existing worn 10" CS SCH/40.</description>
<LookAt>
<longitude>-102.8754448804377</longitude>
<latitude>31.59216413663673</latitude>
<altitude>0</altitude>
<heading>0.02738240047835951</heading>
<tilt>19.33959001588264</tilt>
<range>589.3935585480306</range>
<ns1:altitudeMode>relativeToSeaFloor</ns1:altitudeMode>
</LookAt>
<styleUrl>#msn_C</styleUrl>
<Point>
<ns1:drawOrder>1</ns1:drawOrder>
<coordinates>-102.8767681214162,31.59192479909473,0</coordinates>
</Point>
<ns2:link rel="app" href="https://www.google.com/earth/about/versions/#earth-pro" title="Google Earth Pro 7.3.6.10441" />
</Placemark>
</Document>
<Document>
<name>Original - Rig PL wilth existing Azul PL.kmz</name>
<StyleMap id="msn_ylw-pushpin89">
<Pair>
<key>normal</key>
<styleUrl>#sn_ylw-pushpin96</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#sh_ylw-pushpin94</styleUrl>
</Pair>
</StyleMap>
<Style id="sh_ylw-pushpin94">
<IconStyle>
<scale>1.3</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels" />
</IconStyle>
<LineStyle>
<color>ff0000ff</color>
<width>3</width>
</LineStyle>
</Style>
<Style id="sn_ylw-pushpin96">
<IconStyle>
<scale>1.1</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels" />
</IconStyle>
<LineStyle>
<color>ff0000ff</color>
<width>3</width>
</LineStyle>
</Style>
<Placemark>
<name>Original - Rig PL wilth existing Azul PL</name>
<styleUrl>#msn_ylw-pushpin89</styleUrl>
<LineString>
<tessellate>1</tessellate>
<coordinates>
-103.1381372489133,31.65053381256015,0 -103.1371939568141,31.65078110147886,0 -103.135379572764,31.65137766837301,0 -103.1216582043267,31.65501734857683,0 -103.1267090001346,31.66820232963991,0 -103.1254217441416,31.66857428692562,0 -103.097869762411,31.68730649748832,0 -103.0880082019376,31.69401658057209,0 -103.0201699677156,31.71262120882616,0 -103.0199628293717,31.71246042343891,0 -103.0197573453124,31.71229701578655,0 -103.0195184188874,31.71245394748738,0 -102.9041726111906,31.63091832800928,0 -102.8944523887092,31.6165910414367,0 -102.8811494904473,31.59718777074555,0 -102.8680137394599,31.58231202839571,0 -102.8516886598823,31.56931339800882,0 -102.7180735327187,31.48448961907497,0 -102.7283930769256,31.51503752317085,0 -102.7117114856601,31.51898260340799,0 -102.7165257753119,31.5330847136263,0 -102.7003948024066,31.53686069788628,0 -102.7095293371793,31.56540947523578,0 -102.6901307183483,31.58496921843023,0 -102.6955769601148,31.60301750051444,0
</coordinates>
</LineString>
<ns2:link rel="app" href="https://www.google.com/earth/about/versions/#earth-pro" title="Google Earth Pro 7.3.6.10441" />
</Placemark>
</Document>
</Document></kml>

View File