added datasim and kmz tools
This commit is contained in:
133
Code Snippets/dataSim.py
Normal file
133
Code Snippets/dataSim.py
Normal 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)
|
||||||
|
|
||||||
|
# -------------------------------------------------------------
|
||||||
|
# Clean‑up
|
||||||
|
# -------------------------------------------------------------
|
||||||
|
client.loop_stop()
|
||||||
|
client.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
BIN
Code Snippets/data_estimation_calculator.xlsx
Normal file
BIN
Code Snippets/data_estimation_calculator.xlsx
Normal file
Binary file not shown.
153
Code Snippets/network_usage_per_process.py
Normal file
153
Code Snippets/network_usage_per_process.py
Normal 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
|
||||||
142
Code Snippets/rrig_kmz/breakout_kml.py
Normal file
142
Code Snippets/rrig_kmz/breakout_kml.py
Normal 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 that’s 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}")
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Entry‑point
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
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()
|
||||||
BIN
Code Snippets/rrig_kmz/kmz/Node C Monahans Booster Station.kmz
Normal file
BIN
Code Snippets/rrig_kmz/kmz/Node C Monahans Booster Station.kmz
Normal file
Binary file not shown.
Binary file not shown.
BIN
Code Snippets/rrig_kmz/kmz/Node F US Silica.kmz
Normal file
BIN
Code Snippets/rrig_kmz/kmz/Node F US Silica.kmz
Normal file
Binary file not shown.
BIN
Code Snippets/rrig_kmz/kmz/Node X Cipher Interconnect.kmz
Normal file
BIN
Code Snippets/rrig_kmz/kmz/Node X Cipher Interconnect.kmz
Normal file
Binary file not shown.
Binary file not shown.
BIN
Code Snippets/rrig_kmz/kmz/RRIG Water Pump Station.kmz
Normal file
BIN
Code Snippets/rrig_kmz/kmz/RRIG Water Pump Station.kmz
Normal file
Binary file not shown.
120
Code Snippets/rrig_kmz/kmz_combine.py
Normal file
120
Code Snippets/rrig_kmz/kmz_combine.py
Normal 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 (non‑recursive)."""
|
||||||
|
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 (case‑insensitive)
|
||||||
|
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 top‑level 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)
|
||||||
3
Code Snippets/rrig_kmz/linestring.txt
Normal file
3
Code Snippets/rrig_kmz/linestring.txt
Normal 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]]
|
||||||
|
|
||||||
5
Code Snippets/rrig_kmz/marker.txt
Normal file
5
Code Snippets/rrig_kmz/marker.txt
Normal 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
|
||||||
352
Code Snippets/rrig_kmz/out.kml
Normal file
352
Code Snippets/rrig_kmz/out.kml
Normal 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>
|
||||||
0
Code Snippets/rrig_kmz/polygon.txt
Normal file
0
Code Snippets/rrig_kmz/polygon.txt
Normal file
Reference in New Issue
Block a user