diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d3629c79..d3b17031 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -43,7 +43,7 @@ repos:
     hooks:
       - id: absolufy-imports
   - repo: https://github.com/astral-sh/ruff-pre-commit
-    rev: 'v0.7.4'
+    rev: 'v0.8.0'
     hooks:
       - id: ruff
       - id: ruff-format
diff --git a/README.rst b/README.rst
index 3fe99453..55ecf1bc 100644
--- a/README.rst
+++ b/README.rst
@@ -3,13 +3,15 @@ Introduction
 
 .. inclusion-marker-do-not-remove
 
-KML is an XML geospatial data format and an OGC_ standard that deserves a canonical python implementation.
+KML is an XML geospatial data format and an OGC_ standard that deserves a canonical
+python implementation.
 
 Fastkml is a library to read, write and manipulate KML files. It aims to keep
 it simple and fast (using lxml_ if available). Fast refers to the time you
 spend to write and read KML files as well as the time you spend to get
 acquainted to the library or to create KML objects. It aims to provide all of
 the functionality that KML clients such as `Marble <https://marble.kde.org/>`_,
+`NASA WorldWind <https://github.com/NASAWorldWind>`_,
 `Cesium JS <https://cesium.com/>`_, `OpenLayers <https://openlayers.org/>`_,
 `Google Maps <http://maps.google.com/>`_, and
 `Google Earth <http://earth.google.com/>`_ support.
@@ -18,9 +20,12 @@ For more details about the KML Specification, check out the `KML Reference
 <https://developers.google.com/kml/documentation/kmlreference>`_ on the Google
 developers site.
 
-Geometries are handled as pygeoif_ objects.
+Geometries are handled as pygeoif_ objects, which are compatible with any geometry that
+implements the ``__geo_interface__`` protocol, such as shapely_.
 
-Fastkml is continually tested
+Fastkml is tested on `CPython <https://python.org>`_ and
+`PyPy <https://www.pypy.org/>`_, but it should work on alternative
+Python implementations (that implement the language specification *>=3.8*) as well.
 
 |test| |hypothesis| |cov| |black| |mypy| |commit|
 
@@ -48,9 +53,9 @@ Fastkml is continually tested
    :target: https://github.com/pre-commit/pre-commit
    :alt: pre-commit
 
-Is Maintained and documented:
+Is maintained and documented:
 
-|pypi| |status| |license| |doc| |stats| |pyversion| |pyimpl| |dependencies| |downloads|
+|pypi| |conda-forge| |status| |license| |doc| |stats| |pyversion| |pyimpl| |dependencies| |downloads|
 
 .. |pypi| image:: https://img.shields.io/pypi/v/fastkml.svg
     :target: https://pypi.python.org/pypi/fastkml
@@ -88,6 +93,9 @@ Is Maintained and documented:
     :target: https://pepy.tech/project/fastkml
     :alt: Downloads
 
+.. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/fastkml.svg
+    :target: https://anaconda.org/conda-forge/fastkml
+    :alt: Conda-Forge
 
 Documentation
 =============
@@ -130,3 +138,4 @@ Please submit a PR with the features you'd like to see implemented.
 .. _lxml: https://pypi.python.org/pypi/lxml
 .. _arrow: https://pypi.python.org/pypi/arrow
 .. _OGC: https://www.ogc.org/standard/kml/
+.. _shapely: https://shapely.readthedocs.io/
diff --git a/_typos.toml b/_typos.toml
index a1cb930e..269e7f28 100644
--- a/_typos.toml
+++ b/_typos.toml
@@ -1,3 +1,12 @@
+[default]
+extend-ignore-identifiers-re = [
+    "04AFE6060F147CE66FBD",
+    "Lod",
+    "lod",
+]
+
+
+
 [default.extend-words]
 lod = "lod"
 Lod = "Lod"
diff --git a/docs/HISTORY.rst b/docs/HISTORY.rst
index 4d742416..4e171fa8 100644
--- a/docs/HISTORY.rst
+++ b/docs/HISTORY.rst
@@ -1,7 +1,15 @@
 Changelog
 =========
 
-1.0 (unreleased)
+1.1.0 (unreleased)
+----------------------
+
+- Add support for ScreenOverlay and Model.
+- allow parsing kml files without namespace declarations.
+- Add support for NetworkLinkControl. [Apurva Banka]
+
+
+1.0 (2024/11/19)
 -----------------
 
 - Drop Python 2 support
@@ -11,6 +19,7 @@ Changelog
 - refactor
 - Use arrow instead of dateutil
 - Add an informative ``__repr__``
+- Change the ``from_string`` method to a class method which returns a new instance.
 
 0.12 (2020/09/23)
 -----------------
diff --git a/docs/Makefile b/docs/Makefile
index 38879a67..d4bb2cbb 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -1,177 +1,20 @@
-# Makefile for Sphinx documentation
+# Minimal makefile for Sphinx documentation
 #
 
-# You can set these variables from the command line.
-SPHINXOPTS    =
-SPHINXBUILD   = sphinx-build
-PAPER         =
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
+SOURCEDIR     = .
 BUILDDIR      = _build
 
-# User-friendly check for sphinx-build
-ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
-$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
-endif
-
-# Internal variables.
-PAPEROPT_a4     = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
-
+# Put it first so that "make" without argument is like "make help".
 help:
-	@echo "Please use \`make <target>' where <target> is one of"
-	@echo "  html       to make standalone HTML files"
-	@echo "  dirhtml    to make HTML files named index.html in directories"
-	@echo "  singlehtml to make a single large HTML file"
-	@echo "  pickle     to make pickle files"
-	@echo "  json       to make JSON files"
-	@echo "  htmlhelp   to make HTML files and a HTML help project"
-	@echo "  qthelp     to make HTML files and a qthelp project"
-	@echo "  devhelp    to make HTML files and a Devhelp project"
-	@echo "  epub       to make an epub"
-	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
-	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
-	@echo "  latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
-	@echo "  text       to make text files"
-	@echo "  man        to make manual pages"
-	@echo "  texinfo    to make Texinfo files"
-	@echo "  info       to make Texinfo files and run them through makeinfo"
-	@echo "  gettext    to make PO message catalogs"
-	@echo "  changes    to make an overview of all changed/added/deprecated items"
-	@echo "  xml        to make Docutils-native XML files"
-	@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"
-	@echo "  linkcheck  to check all external links for integrity"
-	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
-
-clean:
-	rm -rf $(BUILDDIR)/*
-
-html:
-	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
-	@echo
-	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
-	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
-	@echo
-	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-singlehtml:
-	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
-	@echo
-	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-pickle:
-	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
-	@echo
-	@echo "Build finished; now you can process the pickle files."
-
-json:
-	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
-	@echo
-	@echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
-	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
-	@echo
-	@echo "Build finished; now you can run HTML Help Workshop with the" \
-	      ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
-	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
-	@echo
-	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
-	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
-	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/FastKML.qhcp"
-	@echo "To view the help file:"
-	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/FastKML.qhc"
-
-devhelp:
-	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
-	@echo
-	@echo "Build finished."
-	@echo "To view the help file:"
-	@echo "# mkdir -p $$HOME/.local/share/devhelp/FastKML"
-	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/FastKML"
-	@echo "# devhelp"
-
-epub:
-	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
-	@echo
-	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-latex:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo
-	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
-	@echo "Run \`make' in that directory to run these through (pdf)latex" \
-	      "(use \`make latexpdf' here to do that automatically)."
-
-latexpdf:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo "Running LaTeX files through pdflatex..."
-	$(MAKE) -C $(BUILDDIR)/latex all-pdf
-	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-latexpdfja:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo "Running LaTeX files through platex and dvipdfmx..."
-	$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
-	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-text:
-	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
-	@echo
-	@echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-man:
-	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
-	@echo
-	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-texinfo:
-	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
-	@echo
-	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
-	@echo "Run \`make' in that directory to run these through makeinfo" \
-	      "(use \`make info' here to do that automatically)."
-
-info:
-	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
-	@echo "Running Texinfo files through makeinfo..."
-	make -C $(BUILDDIR)/texinfo info
-	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
-
-gettext:
-	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
-	@echo
-	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
-
-changes:
-	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
-	@echo
-	@echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
-	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
-	@echo
-	@echo "Link check complete; look for any errors in the above output " \
-	      "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
-	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
-	@echo "Testing of doctests in the sources finished, look at the " \
-	      "results in $(BUILDDIR)/doctest/output.txt."
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
 
-xml:
-	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
-	@echo
-	@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+.PHONY: help Makefile
 
-pseudoxml:
-	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
-	@echo
-	@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/conf.py b/docs/conf.py
index 53caa139..07251bbd 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,33 +1,36 @@
+# noqa: INP001, D100
+# Configuration file for the Sphinx documentation builder.
 #
-# FastKML documentation build configuration file, created by
-# sphinx-quickstart on Mon Oct 13 22:24:07 2014.
-#
-# This file is execfile()d with the current directory set to its
-# containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
 
-import os
+# -- Project information -----------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+
+import pathlib
 import sys
 
+DOC_ROOT = pathlib.Path(__file__).parent
+PROJECT_ROOT = DOC_ROOT.parent
+
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-sys.path.insert(0, os.path.abspath(".."))
-from fastkml import about
+sys.path.insert(0, str(PROJECT_ROOT))
+from fastkml import about  # noqa: E402
 
-# -- General configuration ------------------------------------------------
+# General information about the project.
+project = "FastKML"
+copyright = "2014 -2024, Christian Ledermann & Ian Lee"  # noqa: A001
+author = "Christian Ledermann"
+# The short X.Y version.
+version = ".".join(about.__version__.split(".")[:2])
+# The full version, including alpha/beta/rc tags.
+release = about.__version__
 
-# If your documentation needs a minimal Sphinx version, state it here.
-# needs_sphinx = '1.0'
+# -- General configuration ---------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
 
-# Add any Sphinx extension module names here, as strings. They can be
-# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
-# ones.
 extensions = [
     "sphinx.ext.autodoc",
     "sphinx.ext.doctest",
@@ -38,75 +41,21 @@
 ]
 autosummary_generate = True
 
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ["_templates"]
-
-# The suffix of source filenames.
-source_suffix = ".rst"
-
-# The encoding of source files.
-# source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = "index"
 
-# General information about the project.
-project = "FastKML"
-copyright = "2014 -2024, Christian Ledermann & Ian Lee"
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = ".".join(about.__version__.split(".")[:2])
-# The full version, including alpha/beta/rc tags.
-release = about.__version__
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-# language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-# today = ''
-# Else, today_fmt is used as the format for a strftime call.
-# today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = ["_build"]
-
-# The reST default role (used for this markup: `text`) to use for all
-# documents.
-# default_role = None
+templates_path = ["_templates"]
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
 
-# If true, '()' will be appended to :func: etc. cross-reference text.
-# add_function_parentheses = True
+root_doc = "index"
 
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-# add_module_names = True
 
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-# show_authors = False
+# -- Options for HTML output -------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
 
 # The name of the Pygments (syntax highlighting) style to use.
 pygments_style = "sphinx"
 
-# A list of ignored prefixes for module index sorting.
-# modindex_common_prefix = []
-
-# If true, keep warnings as "system message" paragraphs in the built documents.
-# keep_warnings = False
-
-
-# -- Options for HTML output ----------------------------------------------
-
-# The theme to use for HTML and HTML Help pages.  See the documentation for
-# a list of builtin themes.
-html_theme = "default"
+html_theme = "alabaster"
+html_static_path = ["_static"]
 try:
     import sphinx_rtd_theme
 
@@ -115,67 +64,6 @@
 except ImportError:
     pass
 
-# Theme options are theme-specific and customize the look and feel of a theme
-# further.  For a list of options available for each theme, see the
-# documentation.
-# html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-# html_theme_path = []
-
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
-# html_title = None
-
-# A shorter title for the navigation bar.  Default is the same as html_title.
-# html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-# html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-# html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-# html_static_path = ['_static']
-
-# Add any extra paths that contain custom files (such as robots.txt or
-# .htaccess) here, relative to this directory. These files are copied
-# directly to the root of the documentation.
-# html_extra_path = []
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-# html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-# html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-# html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-# html_additional_pages = {}
-
-# If false, no module index is generated.
-# html_domain_indices = True
-
-# If false, no index is generated.
-# html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-# html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-# html_show_sourcelink = True
-
 html_context = {
     "display_github": True,  # Integrate GitHub
     "github_user": "cleder",  # Username
@@ -183,107 +71,3 @@
     "github_version": "main",  # Version
     "conf_py_path": "/docs/",  # Path in the checkout to the docs root
 }
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-# html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-# html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it.  The value of this option must be the
-# base URL from which the finished HTML is served.
-# html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-# html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = "FastKMLdoc"
-
-
-# -- Options for LaTeX output ---------------------------------------------
-
-latex_elements = {
-    # The paper size ('letterpaper' or 'a4paper').
-    # 'papersize': 'letterpaper',
-    # The font size ('10pt', '11pt' or '12pt').
-    # 'pointsize': '10pt',
-    # Additional stuff for the LaTeX preamble.
-    # 'preamble': '',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title,
-#  author, documentclass [howto, manual, or own class]).
-latex_documents = [
-    (
-        "index",
-        "FastKML.tex",
-        "FastKML Documentation",
-        r"Christian Ledermann \& Ian Lee",
-        "manual",
-    ),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-# latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-# latex_use_parts = False
-
-# If true, show page references after internal links.
-# latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-# latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-# latex_appendices = []
-
-# If false, no module index is generated.
-# latex_domain_indices = True
-
-
-# -- Options for manual page output ---------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
-    ("index", "fastkml", "FastKML Documentation", ["Christian Ledermann & Ian Lee"], 1),
-]
-
-# If true, show URL addresses after external links.
-# man_show_urls = False
-
-
-# -- Options for Texinfo output -------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-#  dir menu entry, description, category)
-texinfo_documents = [
-    (
-        "index",
-        "FastKML",
-        "FastKML Documentation",
-        "Christian Ledermann & Ian Lee",
-        "FastKML",
-        "One line description of project.",
-        "Miscellaneous",
-    ),
-]
-
-# Documents to append as an appendix to all manuals.
-# texinfo_appendices = []
-
-# If false, no module index is generated.
-# texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-# texinfo_show_urls = 'footnote'
-
-# If true, do not generate a @detailmenu in the "Top" node's menu.
-# texinfo_no_detailmenu = False
diff --git a/docs/create_kml_files.rst b/docs/create_kml_files.rst
index f722aa20..d608a0ee 100644
--- a/docs/create_kml_files.rst
+++ b/docs/create_kml_files.rst
@@ -109,10 +109,8 @@ Finally, we create the KML object and write it to a file:
     >>> document = fastkml.containers.Document(features=placemarks)
     >>> kml = fastkml.KML(features=[document])
     >>> outfile = pathlib.Path("co2_per_capita_2020.kml")
-    >>> with outfile.open("w") as f:
-    ...     f.write(kml.to_string(prettyprint=True, precision=3))  # doctest: +ELLIPSIS
-    ...
-    4...
+    >>> kml.write(outfile, prettyprint=True, precision=3)  # doctest: +ELLIPSIS
+
 
 The resulting KML file can be opened in Google Earth or any other KML viewer.
 
@@ -232,10 +230,7 @@ Finally, we create the KML object and write it to a file:
     >>> document = fastkml.containers.Document(features=folders, styles=styles)
     >>> kml = fastkml.KML(features=[document])
     >>> outfile = pathlib.Path("co2_growth_1995_2022.kml")
-    >>> with outfile.open("w") as f:
-    ...     f.write(kml.to_string(prettyprint=True, precision=3))  # doctest: +ELLIPSIS
-    ...
-    1...
+    >>> kml.write(outfile, prettyprint=True, precision=3)
 
 
 You can open the resulting KML file in Google Earth Desktop and use the time slider to
diff --git a/docs/fastkml.rst b/docs/fastkml.rst
index 96553566..d4a13808 100644
--- a/docs/fastkml.rst
+++ b/docs/fastkml.rst
@@ -30,14 +30,13 @@ fastkml.registry
 -----------------------
 
 .. automodule:: fastkml.registry
-   :members:
+   :members: RegistryItem,Registry
    :undoc-members:
    :show-inheritance:
 
-.. autoclass:: fastkml.registry::Registry
-    :members: register, get
-    :undoc-members:
-    :show-inheritance:
+   .. autodata:: registry
+      :no-value:
+
 
 fastkml.kml\_base
 ------------------------
@@ -158,6 +157,15 @@ fastkml.mixins
    :undoc-members:
    :show-inheritance:
 
+fastkml.model
+--------------------
+
+.. automodule:: fastkml.model
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+
 fastkml.overlays
 -----------------------
 
diff --git a/docs/index.rst b/docs/index.rst
index acdaf036..d438380c 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -26,7 +26,9 @@ requirements, namely:
    create_kml_files
    working_with_kml
    configuration
+   upgrading
    fastkml
    contributing
+   kml
    alternatives
    HISTORY
diff --git a/docs/kml.rst b/docs/kml.rst
new file mode 100644
index 00000000..29f8133b
--- /dev/null
+++ b/docs/kml.rst
@@ -0,0 +1,13 @@
+KML Resources and Tutorials
+===========================
+
+Learning KML can be straightforward with the right resources.
+Here are some of the best sources:
+
+
+`Google Developers - KML  <https://developers.google.com/kml>`_ provides a comprehensive
+guide to KML, including tutorials, reference, and examples.
+
+
+The `FME Support Center's section on OGC and Google KML <https://support.safe.com/hc/en-us/sections/25407358419469-OGC-Google-KML>`_
+provides detailed guidance for creating, styling, and optimizing KML files.
diff --git a/docs/network.kml b/docs/network.kml
new file mode 100644
index 00000000..48f6004d
--- /dev/null
+++ b/docs/network.kml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kml xmlns="http://www.opengis.net/kml/2.2"
+  xmlns:atom="http://www.w3.org/2005/Atom"
+  xmlns:xal="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"
+  hint="A processing hint for a KML consumer">
+    <NetworkLinkControl>
+      <minRefreshPeriod>43200</minRefreshPeriod>
+      <maxSessionLength>-1</maxSessionLength>
+      <linkSnippet>
+      <![CDATA[
+      <p xmlns="http://www.w3.org/1999/xhtml">A snippet of <a href="http://www.w3.org/TR/xhtml-basic/">XHTML</a></p>
+      ]]>
+      </linkSnippet>
+      <expires>2008-05-30</expires>
+    </NetworkLinkControl>
+</kml>
\ No newline at end of file
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
index 34b2355e..fb837a58 100644
--- a/docs/quickstart.rst
+++ b/docs/quickstart.rst
@@ -17,25 +17,24 @@ First we import the necessary modules:
     >>> from fastkml import kml
     >>> from pygeoif.geometry import Polygon
 
-Create a KML object and set the namespace:
+Create a KML object:
 
 .. code-block:: pycon
 
     >>> k = kml.KML()
-    >>> ns = "{http://www.opengis.net/kml/2.2}"
 
 Create a KML Document and add it to the KML root object:
 
 .. code-block:: pycon
 
-    >>> d = kml.Document(ns=ns, id="docid", name="doc name", description="doc description")
+    >>> d = kml.Document(id="docid", name="doc name", description="doc description")
     >>> k.append(d)
 
 Create a KML Folder and add it to the Document:
 
 .. code-block:: pycon
 
-    >>> f = kml.Folder(ns=ns, id="fid", name="f name", description="f description")
+    >>> f = kml.Folder(id="fid", name="f name", description="f description")
     >>> d.append(f)
 
 Create a KML Folder and nest it in the first Folder:
@@ -43,7 +42,7 @@ Create a KML Folder and nest it in the first Folder:
 .. code-block:: pycon
 
     >>> nf = kml.Folder(
-    ...     ns=ns, id="nested-fid", name="nested f name", description="nested f description"
+    ...     id="nested-fid", name="nested f name", description="nested f description"
     ... )
     >>> f.append(nf)
 
@@ -51,7 +50,7 @@ Create a second KML Folder within the Document:
 
 .. code-block:: pycon
 
-    >>> f2 = kml.Folder(ns=ns, id="id2", name="name2", description="description2")
+    >>> f2 = kml.Folder(id="id2", name="name2", description="description2")
     >>> d.append(f2)
 
 Create a KML Placemark with a simple polygon geometry and add it to the second Folder:
@@ -59,9 +58,7 @@ Create a KML Placemark with a simple polygon geometry and add it to the second F
 .. code-block:: pycon
 
     >>> polygon = Polygon([(0, 0, 0), (1, 1, 0), (1, 0, 1)])
-    >>> p = kml.Placemark(
-    ...     ns=ns, id="id", name="name", description="description", geometry=polygon
-    ... )
+    >>> p = kml.Placemark(id="id", name="name", description="description", geometry=polygon)
     >>> f2.append(p)
 
 Finally, print out the KML object as a string:
@@ -141,6 +138,17 @@ Read in the KML string
 
     >>> k = kml.KML.from_string(doc)
 
+
+.. note::
+
+    To read a KML file directly, you can use the parse method:
+
+    .. code-block:: Python
+
+        k = kml.KML.parse("path/to/file.kml")
+
+
+
 Next we perform some simple sanity checks, such as checking the number of features.
 
 .. code-block:: pycon
@@ -206,3 +214,11 @@ Finally, print out the KML object as a string:
       </Document>
     </kml>
     <BLANKLINE>
+
+.. note::
+
+    To save the KML object to a file, you can use the write method:
+
+    .. code-block:: Python
+
+        k.write("path/to/file.kml")
diff --git a/docs/upgrading.rst b/docs/upgrading.rst
new file mode 100644
index 00000000..82a002ac
--- /dev/null
+++ b/docs/upgrading.rst
@@ -0,0 +1,28 @@
+Upgrading from older versions of FastKML
+========================================
+
+Q: I updated from 0.12 to 1.0.0 and now getting the following errors when using
+``parse()``::
+
+    File "src/lxml/etree.pyx", line 3701, in lxml.etree._Validator.assert_
+    AssertionError: Element ...
+
+A: Your KML does not validate against the XML Schema.
+You can read it without validations by passing ``validate=False`` or ``strict=False``
+to the parse method::
+
+    from fastkml.kml import KML
+    doc = KML.parse('path/to/your/file.kml', strict=False)
+    # or
+    doc = KML.parse('path/to/your/file.kml', validate=False)
+
+With version 1.0, ``.from_string()`` is a class method that returns a new object.
+
+In fastkml 0.x::
+
+    postcode_kml = kml.KML()
+    postcode_kml.from_string(kml_file.read())
+
+Becomes in 1.0::
+
+    postcode_kml = kml.KML.from_string(kml_file.read())
diff --git a/docs/working_with_kml.rst b/docs/working_with_kml.rst
index 3da5942e..b810f79f 100644
--- a/docs/working_with_kml.rst
+++ b/docs/working_with_kml.rst
@@ -50,8 +50,12 @@ We could also search for all Points, which will also return the Points inside th
 
 ``find_all`` can also search for arbitrary elements by their attributes, by passing the
 attribute name and value as keyword arguments.
-``find`` is a shortcut for ``find_all`` that returns the first element found, which is
-useful when we know there is only one element that matches the search criteria.
+
+.. note::
+
+    ``find`` is a shortcut for ``find_all`` that returns the first element found,
+    which is useful when we know there is only one element that matches the search
+    criteria.
 
 .. code-block:: pycon
 
@@ -123,7 +127,7 @@ We need to register the attributes of the KML object to be able to parse it:
     >>> registry.register(
     ...     CascadingStyle,
     ...     RegistryItem(
-    ...         ns_ids=("kml",),
+    ...         ns_ids=("kml", ""),
     ...         attr_name="style",
     ...         node_name="Style",
     ...         classes=(Style,),
@@ -152,8 +156,10 @@ And register the new element with the KML Document object:
 
 The CascadingStyle object is now part of the KML document and can be accessed like any
 other element.
-When parsing the document we have to skip the validation as the ``gx:CascadingStyle`` is
-not in the XSD Schema.
+
+.. note::
+    When parsing the document we have to skip the validation by passing ``validate=False``
+    to ``KML.parse`` as the ``gx:CascadingStyle`` is not in the XSD Schema.
 
 Create a new KML object and confirm that the new element is parsed correctly:
 
diff --git a/examples/owid-co2-data.csv b/examples/owid-co2-data.csv
index f6f5e1a3..65c696f6 100644
--- a/examples/owid-co2-data.csv
+++ b/examples/owid-co2-data.csv
@@ -197,7 +197,7 @@ year,iso_code,co2_per_capita
 1995,THA,2.586
 1995,TGO,0.274
 1995,TON,0.953
-1995,TO,11.265
+1995,TTO,11.265
 1995,TUN,1.751
 1995,TUR,3.058
 1995,TKM,8.179
@@ -416,7 +416,7 @@ year,iso_code,co2_per_capita
 1996,THA,2.883
 1996,TGO,0.285
 1996,TON,0.768
-1996,TO,14.053
+1996,TTO,14.053
 1996,TUN,1.762
 1996,TUR,3.311
 1996,TKM,7.36
@@ -635,7 +635,7 @@ year,iso_code,co2_per_capita
 1997,THA,2.971
 1997,TGO,0.191
 1997,TON,0.983
-1997,TO,14.346
+1997,TTO,14.346
 1997,TUN,1.801
 1997,TUR,3.461
 1997,TKM,7.149
@@ -854,7 +854,7 @@ year,iso_code,co2_per_capita
 1998,THA,2.587
 1998,TGO,0.273
 1998,TON,0.868
-1998,TO,15.05
+1998,TTO,15.05
 1998,TUN,1.838
 1998,TUR,3.408
 1998,TKM,7.547
@@ -1073,7 +1073,7 @@ year,iso_code,co2_per_capita
 1999,THA,2.678
 1999,TGO,0.38
 1999,TON,1.078
-1999,TO,17.026
+1999,TTO,17.026
 1999,TUN,1.904
 1999,TUR,3.291
 1999,TKM,8.842
@@ -1292,7 +1292,7 @@ year,iso_code,co2_per_capita
 2000,THA,2.654
 2000,TGO,0.266
 2000,TON,0.928
-2000,TO,18.29
+2000,TTO,18.29
 2000,TUN,1.977
 2000,TUR,3.586
 2000,TKM,8.615
@@ -1511,7 +1511,7 @@ year,iso_code,co2_per_capita
 2001,THA,2.706
 2001,TGO,0.225
 2001,TON,0.852
-2001,TO,19.989
+2001,TTO,19.989
 2001,TUN,2.044
 2001,TUR,3.282
 2001,TKM,7.343
@@ -1730,7 +1730,7 @@ year,iso_code,co2_per_capita
 2002,THA,2.875
 2002,TGO,0.25
 2002,TON,0.988
-2002,TO,21.324
+2002,TTO,21.324
 2002,TUN,2.049
 2002,TUR,3.352
 2002,TKM,6.412
@@ -1949,7 +1949,7 @@ year,iso_code,co2_per_capita
 2003,THA,2.95
 2003,TGO,0.333
 2003,TON,1.123
-2003,TO,23.931
+2003,TTO,23.931
 2003,TUN,2.074
 2003,TUR,3.541
 2003,TKM,8.524
@@ -2168,7 +2168,7 @@ year,iso_code,co2_per_capita
 2004,THA,3.175
 2004,TGO,0.313
 2004,TON,1.046
-2004,TO,24.032
+2004,TTO,24.032
 2004,TUN,2.147
 2004,TUR,3.611
 2004,TKM,10.24
@@ -2387,7 +2387,7 @@ year,iso_code,co2_per_capita
 2005,THA,3.256
 2005,TGO,0.301
 2005,TON,1.075
-2005,TO,27.922
+2005,TTO,27.922
 2005,TUN,2.171
 2005,TUR,3.855
 2005,TKM,9.81
@@ -2606,7 +2606,7 @@ year,iso_code,co2_per_capita
 2006,THA,3.254
 2006,TGO,0.255
 2006,TON,1.208
-2006,TO,31.031
+2006,TTO,31.031
 2006,TUN,2.191
 2006,TUR,4.057
 2006,TKM,9.992
@@ -2825,7 +2825,7 @@ year,iso_code,co2_per_capita
 2007,THA,3.353
 2007,TGO,0.251
 2007,TON,1.065
-2007,TO,32.915
+2007,TTO,32.915
 2007,TUN,2.324
 2007,TUR,4.452
 2007,TKM,9.788
@@ -3044,7 +3044,7 @@ year,iso_code,co2_per_capita
 2008,THA,3.331
 2008,TGO,0.245
 2008,TON,1.131
-2008,TO,31.924
+2008,TTO,31.924
 2008,TUN,2.391
 2008,TUR,4.355
 2008,TKM,11.654
@@ -3263,7 +3263,7 @@ year,iso_code,co2_per_capita
 2009,THA,3.374
 2009,TGO,0.428
 2009,TON,1.231
-2009,TO,31.717
+2009,TTO,31.717
 2009,TUN,2.357
 2009,TUR,4.381
 2009,TKM,10.126
@@ -3482,7 +3482,7 @@ year,iso_code,co2_per_capita
 2010,THA,3.53
 2010,TGO,0.395
 2010,TON,1.092
-2010,TO,33.406
+2010,TTO,33.406
 2010,TUN,2.583
 2010,TUR,4.32
 2010,TKM,11.235
@@ -3701,7 +3701,7 @@ year,iso_code,co2_per_capita
 2011,THA,3.564
 2011,TGO,0.371
 2011,TON,0.953
-2011,TO,33.146
+2011,TTO,33.146
 2011,TUN,2.396
 2011,TUR,4.612
 2011,TKM,12.154
@@ -3920,7 +3920,7 @@ year,iso_code,co2_per_capita
 2012,THA,3.794
 2012,TGO,0.32
 2012,TON,0.988
-2012,TO,32.282
+2012,TTO,32.282
 2012,TUN,2.547
 2012,TUR,4.731
 2012,TKM,12.263
@@ -4139,7 +4139,7 @@ year,iso_code,co2_per_capita
 2013,THA,3.791
 2013,TGO,0.231
 2013,TON,1.06
-2013,TO,31.806
+2013,TTO,31.806
 2013,TUN,2.507
 2013,TUR,4.536
 2013,TKM,11.572
@@ -4358,7 +4358,7 @@ year,iso_code,co2_per_capita
 2014,THA,3.895
 2014,TGO,0.212
 2014,TON,1.065
-2014,TO,32.324
+2014,TTO,32.324
 2014,TUN,2.598
 2014,TUR,4.66
 2014,TKM,11.089
@@ -4577,7 +4577,7 @@ year,iso_code,co2_per_capita
 2015,THA,3.942
 2015,TGO,0.249
 2015,TON,1.105
-2015,TO,31.201
+2015,TTO,31.201
 2015,TUN,2.718
 2015,TUR,4.833
 2015,TKM,11.155
@@ -4796,7 +4796,7 @@ year,iso_code,co2_per_capita
 2016,THA,4.023
 2016,TGO,0.302
 2016,TON,1.178
-2016,TO,27.15
+2016,TTO,27.15
 2016,TUN,2.618
 2016,TUR,5.011
 2016,TKM,10.982
@@ -5015,7 +5015,7 @@ year,iso_code,co2_per_capita
 2017,THA,3.997
 2017,TGO,0.254
 2017,TON,1.286
-2017,TO,27.267
+2017,TTO,27.267
 2017,TUN,2.648
 2017,TUR,5.249
 2017,TKM,10.783
@@ -5234,7 +5234,7 @@ year,iso_code,co2_per_capita
 2018,THA,4.054
 2018,TGO,0.269
 2018,TON,1.289
-2018,TO,26.801
+2018,TTO,26.801
 2018,TUN,2.609
 2018,TUR,5.097
 2018,TKM,10.513
@@ -5453,7 +5453,7 @@ year,iso_code,co2_per_capita
 2019,THA,3.953
 2019,TGO,0.293
 2019,TON,1.536
-2019,TO,26.832
+2019,TTO,26.832
 2019,TUN,2.519
 2019,TUR,4.824
 2019,TKM,10.607
@@ -5672,7 +5672,7 @@ year,iso_code,co2_per_capita
 2020,THA,3.803
 2020,TGO,0.282
 2020,TON,1.74
-2020,TO,23.074
+2020,TTO,23.074
 2020,TUN,2.343
 2020,TUR,4.908
 2020,TKM,10.831
@@ -5891,7 +5891,7 @@ year,iso_code,co2_per_capita
 2021,THA,3.732
 2021,TGO,0.296
 2021,TON,1.803
-2021,TO,23.29
+2021,TTO,23.29
 2021,TUN,2.874
 2021,TUR,5.34
 2021,TKM,11.034
@@ -6110,7 +6110,7 @@ year,iso_code,co2_per_capita
 2022,THA,3.776
 2022,TGO,0.291
 2022,TON,1.769
-2022,TO,22.424
+2022,TTO,22.424
 2022,TUN,2.879
 2022,TUR,5.105
 2022,TKM,11.034
diff --git a/examples/shp2kml.py b/examples/shp2kml.py
index 1b0a1634..f5edb6a7 100755
--- a/examples/shp2kml.py
+++ b/examples/shp2kml.py
@@ -35,7 +35,7 @@
 
 for feature in shp.__geo_interface__["features"]:
     geometry = shape(feature["geometry"])
-    co2_emission = co2_data.get(feature["properties"]["ADM0_A3"], 0)
+    co2_emission = co2_data.get(feature["properties"]["ADM0_ISO"], 0)
     geometry = force_3d(geometry, co2_emission * 100_000)
     kml_geometry = create_kml_geometry(
         geometry,
@@ -44,7 +44,7 @@
     )
     color = random.randint(0, 0xFFFFFF)
     style = fastkml.styles.Style(
-        id=feature["properties"]["ADM0_A3"],
+        id=feature["properties"]["ADM0_ISO"],
         styles=[
             fastkml.styles.LineStyle(color=f"55{color:06X}", width=2),
             fastkml.styles.PolyStyle(
@@ -56,7 +56,7 @@
         ],
     )
 
-    style_url = fastkml.styles.StyleUrl(url=f"#{feature['properties']['ADM0_A3']}")
+    style_url = fastkml.styles.StyleUrl(url=f"#{feature['properties']['ADM0_ISO']}")
     placemark = fastkml.features.Placemark(
         name=feature["properties"]["NAME"],
         description=feature["properties"]["FORMAL_EN"],
@@ -68,5 +68,4 @@
 kml = fastkml.KML(features=[document])
 
 outfile = pathlib.Path("co2_per_capita_2020.kml")
-with outfile.open("w") as f:
-    f.write(kml.to_string(prettyprint=True, precision=3))
+kml.write(outfile, prettyprint=True, precision=3)
diff --git a/examples/shp2kml_timed.py b/examples/shp2kml_timed.py
index 2f7dd343..cc5c9cc3 100755
--- a/examples/shp2kml_timed.py
+++ b/examples/shp2kml_timed.py
@@ -1,4 +1,6 @@
 #!/usr/bin/env python
+from __future__ import annotations
+
 import csv
 import datetime
 import pathlib
@@ -23,7 +25,7 @@
 
 co2_csv = pathlib.Path(examples_dir / "owid-co2-data.csv")
 
-co2_pa = {str(i): {} for i in range(1995, 2023)}
+co2_pa: dict[str, dict[str, float]] = {str(i): {} for i in range(1995, 2023)}
 
 with co2_csv.open() as csvfile:
     reader = csv.DictReader(csvfile)
@@ -36,7 +38,7 @@
 styles = []
 folders = []
 for feature in shp.__geo_interface__["features"]:
-    iso3_code = feature["properties"]["ADM0_A3"]
+    iso3_code = feature["properties"]["ADM0_ISO"]
     geometry = shape(feature["geometry"])
     color = random.randint(0, 0xFFFFFF)
     styles.append(
@@ -54,9 +56,9 @@
     )
     style_url = fastkml.styles.StyleUrl(url=f"#{iso3_code}")
     folder = fastkml.containers.Folder(name=feature["properties"]["NAME"])
-    co2_growth = 0
+    co2_growth = 0.0
     for year in range(1995, 2023):
-        co2_year = co2_pa[str(year)].get(iso3_code, 0)
+        co2_year = co2_pa[str(year)].get(iso3_code, 0.0)
         co2_growth += co2_year
 
         kml_geometry = create_kml_geometry(
@@ -87,6 +89,5 @@
 document = fastkml.containers.Document(features=folders, styles=styles)
 kml = fastkml.KML(features=[document])
 
-outfile = pathlib.Path("co2_growth_1995_2022.kml")
-with outfile.open("w") as f:
-    f.write(kml.to_string(prettyprint=True, precision=3))
+outfile = pathlib.Path("co2_growth_1995_2022.kmz")
+kml.write(outfile, prettyprint=True, precision=3)
diff --git a/examples/simple_example.py b/examples/simple_example.py
index c5b9e139..183d856a 100755
--- a/examples/simple_example.py
+++ b/examples/simple_example.py
@@ -17,7 +17,6 @@ def print_child_features(element, depth=0):
     examples_dir = pathlib.Path(__file__).parent
     fname = pathlib.Path(examples_dir / "KML_Samples.kml")
 
-    with fname.open(encoding="utf-8") as kml_file:
-        k = kml.KML.from_string(kml_file.read().encode("utf-8"))
+    k = kml.KML.parse(fname)
 
     print_child_features(k)
diff --git a/fastkml/__init__.py b/fastkml/__init__.py
index b607e08f..769799dd 100644
--- a/fastkml/__init__.py
+++ b/fastkml/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2012 -2022 Christian Ledermann
+# Copyright (C) 2012 -2024 Christian Ledermann
 #
 # This library is free software; you can redistribute it and/or modify it under
 # the terms of the GNU Lesser General Public License as published by the Free
@@ -35,61 +35,121 @@
 from fastkml.data import ExtendedData
 from fastkml.data import Schema
 from fastkml.data import SchemaData
+from fastkml.data import SimpleData
+from fastkml.data import SimpleField
+from fastkml.features import NetworkLink
 from fastkml.features import Placemark
+from fastkml.features import Snippet
+from fastkml.geometry import Coordinates
+from fastkml.geometry import InnerBoundaryIs
 from fastkml.geometry import LinearRing
 from fastkml.geometry import LineString
 from fastkml.geometry import MultiGeometry
+from fastkml.geometry import OuterBoundaryIs
 from fastkml.geometry import Point
 from fastkml.geometry import Polygon
+from fastkml.geometry import create_kml_geometry
 from fastkml.kml import KML
 from fastkml.links import Icon
 from fastkml.links import Link
+from fastkml.model import Alias
+from fastkml.model import Location
+from fastkml.model import Model
+from fastkml.model import Orientation
+from fastkml.model import ResourceMap
+from fastkml.model import Scale
+from fastkml.network_link_control import NetworkLinkControl
 from fastkml.overlays import GroundOverlay
+from fastkml.overlays import ImagePyramid
+from fastkml.overlays import LatLonBox
+from fastkml.overlays import OverlayXY
 from fastkml.overlays import PhotoOverlay
+from fastkml.overlays import RotationXY
+from fastkml.overlays import ScreenOverlay
+from fastkml.overlays import ScreenXY
+from fastkml.overlays import Size
+from fastkml.overlays import ViewVolume
 from fastkml.styles import BalloonStyle
+from fastkml.styles import HotSpot
 from fastkml.styles import IconStyle
 from fastkml.styles import LabelStyle
 from fastkml.styles import LineStyle
+from fastkml.styles import Pair
 from fastkml.styles import PolyStyle
 from fastkml.styles import Style
 from fastkml.styles import StyleMap
 from fastkml.styles import StyleUrl
+from fastkml.times import KmlDateTime
 from fastkml.times import TimeSpan
 from fastkml.times import TimeStamp
+from fastkml.utils import find
+from fastkml.utils import find_all
+from fastkml.validator import get_schema_parser
+from fastkml.validator import validate
 from fastkml.views import Camera
 from fastkml.views import LookAt
 
 __all__ = [
     "KML",
+    "Alias",
+    "AtomAuthor",
+    "AtomContributor",
+    "AtomLink",
+    "BalloonStyle",
+    "Camera",
+    "Coordinates",
+    "Data",
     "Document",
+    "ExtendedData",
     "Folder",
     "GroundOverlay",
-    "Placemark",
-    "TimeSpan",
-    "TimeStamp",
-    "ExtendedData",
-    "Data",
-    "PhotoOverlay",
-    "Schema",
-    "SchemaData",
-    "StyleUrl",
-    "Style",
-    "StyleMap",
+    "HotSpot",
+    "Icon",
     "IconStyle",
-    "LineStyle",
-    "PolyStyle",
+    "ImagePyramid",
+    "InnerBoundaryIs",
+    "KmlDateTime",
     "LabelStyle",
-    "BalloonStyle",
-    "AtomLink",
-    "Icon",
-    "Link",
-    "Point",
+    "LatLonBox",
     "LineString",
+    "LineStyle",
     "LinearRing",
-    "Polygon",
-    "MultiGeometry",
-    "AtomAuthor",
-    "AtomContributor",
-    "Camera",
+    "Link",
+    "Location",
     "LookAt",
+    "Model",
+    "MultiGeometry",
+    "NetworkLink",
+    "NetworkLinkControl",
+    "Orientation",
+    "OuterBoundaryIs",
+    "OverlayXY",
+    "Pair",
+    "PhotoOverlay",
+    "Placemark",
+    "Point",
+    "PolyStyle",
+    "Polygon",
+    "ResourceMap",
+    "RotationXY",
+    "Scale",
+    "Schema",
+    "SchemaData",
+    "ScreenOverlay",
+    "ScreenXY",
+    "SimpleData",
+    "SimpleField",
+    "Size",
+    "Snippet",
+    "Style",
+    "StyleMap",
+    "StyleUrl",
+    "TimeSpan",
+    "TimeStamp",
+    "ViewVolume",
+    "create_kml_geometry",
+    "find",
+    "find_all",
+    "get_schema_parser",
+    "validate",
 ]
diff --git a/fastkml/about.py b/fastkml/about.py
index ce656dad..aa006175 100644
--- a/fastkml/about.py
+++ b/fastkml/about.py
@@ -19,7 +19,7 @@
 The only purpose of this module is to provide a version number for the package.
 """
 
-__version__ = "1.0.0"
+__version__ = "1.1.0"
 """Fastkml version number."""
 
 __all__ = ["__version__"]
diff --git a/fastkml/base.py b/fastkml/base.py
index ca2c7c21..8fa9d875 100644
--- a/fastkml/base.py
+++ b/fastkml/base.py
@@ -83,8 +83,8 @@ def __init__(
         self.ns: str = (
             self.name_spaces.get(self._default_nsid, "") if ns is None else ns
         )
-        for arg in kwargs:
-            setattr(self, arg, kwargs[arg])
+        for arg, val in kwargs.items():
+            setattr(self, arg, val)
         self.__kwarg_keys = tuple(kwargs.keys())
 
     def __repr__(self) -> str:
diff --git a/fastkml/containers.py b/fastkml/containers.py
index 5041d219..1a3e7737 100644
--- a/fastkml/containers.py
+++ b/fastkml/containers.py
@@ -41,6 +41,7 @@
 from fastkml.helpers import xml_subelement_list_kwarg
 from fastkml.overlays import GroundOverlay
 from fastkml.overlays import PhotoOverlay
+from fastkml.overlays import ScreenOverlay
 from fastkml.registry import RegistryItem
 from fastkml.registry import registry
 from fastkml.styles import Style
@@ -55,6 +56,8 @@
 
 logger = logging.getLogger(__name__)
 
+__all__ = ["Document", "Folder"]
+
 KmlGeometry = Union[
     Point,
     LineString,
@@ -326,10 +329,21 @@ def get_style_by_url(self, style_url: str) -> Optional[Union[Style, StyleMap]]:
 registry.register(
     _Container,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="features",
-        node_name="Folder,Placemark,Document,GroundOverlay,PhotoOverlay,NetworkLink",
-        classes=(Document, Folder, Placemark, GroundOverlay, PhotoOverlay, NetworkLink),
+        node_name=(
+            "Folder,Placemark,Document,GroundOverlay,PhotoOverlay,ScreenOverlay,"
+            "NetworkLink"
+        ),
+        classes=(
+            Document,
+            Folder,
+            Placemark,
+            GroundOverlay,
+            PhotoOverlay,
+            ScreenOverlay,
+            NetworkLink,
+        ),
         get_kwarg=xml_subelement_list_kwarg,
         set_element=xml_subelement_list,
     ),
@@ -337,7 +351,7 @@ def get_style_by_url(self, style_url: str) -> Optional[Union[Style, StyleMap]]:
 registry.register(
     Document,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="schemata",
         node_name="Schema",
         classes=(Schema,),
diff --git a/fastkml/data.py b/fastkml/data.py
index baea3587..573ca074 100644
--- a/fastkml/data.py
+++ b/fastkml/data.py
@@ -50,6 +50,7 @@
     "ExtendedData",
     "Schema",
     "SchemaData",
+    "SimpleData",
     "SimpleField",
 ]
 
@@ -310,7 +311,7 @@ def append(self, field: SimpleField) -> None:
 registry.register(
     Schema,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="fields",
         node_name="SimpleField",
         classes=(SimpleField,),
@@ -441,6 +442,14 @@ def __bool__(self) -> bool:
 
 
 class SimpleData(_XMLObject):
+    """
+    A SimpleData element is a custom data field.
+
+    This element assigns a value to the custom data field identified by the name
+    attribute. The type and name of this custom data field are declared in the
+    ``<Schema>`` element.
+    """
+
     _default_nsid = "kml"
 
     name: Optional[str]
@@ -635,7 +644,7 @@ def append_data(self, data: SimpleData) -> None:
 registry.register(
     SchemaData,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="data",
         node_name="SimpleData",
         classes=(SimpleData,),
@@ -716,7 +725,7 @@ def __bool__(self) -> bool:
 registry.register(
     ExtendedData,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="elements",
         node_name="Data,SchemaData",
         classes=(
diff --git a/fastkml/features.py b/fastkml/features.py
index f8d091a5..af535ec2 100644
--- a/fastkml/features.py
+++ b/fastkml/features.py
@@ -58,6 +58,7 @@
 from fastkml.kml_base import _BaseObject
 from fastkml.links import Link
 from fastkml.mixins import TimeMixin
+from fastkml.model import Model
 from fastkml.registry import RegistryItem
 from fastkml.registry import registry
 from fastkml.styles import Style
@@ -69,7 +70,7 @@
 from fastkml.views import LookAt
 from fastkml.views import Region
 
-__all__ = ["KmlGeometry", "NetworkLink", "Placemark", "Snippet"]
+__all__ = ["NetworkLink", "Placemark", "Snippet"]
 
 logger = logging.getLogger(__name__)
 
@@ -78,6 +79,7 @@
     LineString,
     LinearRing,
     Polygon,
+    Model,
     MultiGeometry,
     gx.MultiTrack,
     gx.Track,
@@ -309,7 +311,7 @@ def __init__(
 registry.register(
     _Feature,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="name",
         node_name="name",
         classes=(str,),
@@ -320,7 +322,7 @@ def __init__(
 registry.register(
     _Feature,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="visibility",
         node_name="visibility",
         classes=(bool,),
@@ -332,7 +334,7 @@ def __init__(
 registry.register(
     _Feature,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="isopen",
         node_name="open",
         classes=(bool,),
@@ -366,7 +368,7 @@ def __init__(
 registry.register(
     _Feature,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="address",
         node_name="address",
         classes=(str,),
@@ -377,7 +379,7 @@ def __init__(
 registry.register(
     _Feature,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="phone_number",
         node_name="phoneNumber",
         classes=(str,),
@@ -388,7 +390,7 @@ def __init__(
 registry.register(
     _Feature,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="snippet",
         node_name="Snippet",
         classes=(Snippet,),
@@ -399,7 +401,7 @@ def __init__(
 registry.register(
     _Feature,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="description",
         node_name="description",
         classes=(str,),
@@ -410,7 +412,7 @@ def __init__(
 registry.register(
     _Feature,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="view",
         node_name="Camera,LookAt",
         classes=(
@@ -424,7 +426,7 @@ def __init__(
 registry.register(
     _Feature,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="times",
         node_name="TimeSpan,TimeStamp",
         classes=(
@@ -438,7 +440,7 @@ def __init__(
 registry.register(
     _Feature,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="style_url",
         node_name="styleUrl",
         classes=(StyleUrl,),
@@ -449,7 +451,7 @@ def __init__(
 registry.register(
     _Feature,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="styles",
         node_name="Style,StyleMap",
         classes=(
@@ -463,7 +465,7 @@ def __init__(
 registry.register(
     _Feature,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="region",
         node_name="region",
         classes=(Region,),
@@ -474,7 +476,7 @@ def __init__(
 registry.register(
     _Feature,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="extended_data",
         node_name="ExtendedData",
         classes=(ExtendedData,),
@@ -662,10 +664,10 @@ def geometry(self) -> Optional[AnyGeometryType]:
 registry.register(
     Placemark,
     RegistryItem(
-        ns_ids=("kml", "gx"),
+        ns_ids=("kml", "gx", ""),
         attr_name="kml_geometry",
         node_name=(
-            "Point,LineString,LinearRing,Polygon,MultiGeometry,"
+            "Point,LineString,LinearRing,Polygon,MultiGeometry,Model,"
             "gx:MultiTrack,gx:Track"
         ),
         classes=(
@@ -674,6 +676,7 @@ def geometry(self) -> Optional[AnyGeometryType]:
             LinearRing,
             Polygon,
             MultiGeometry,
+            Model,
             gx.MultiTrack,
             gx.Track,
         ),
@@ -891,7 +894,7 @@ def __bool__(self) -> bool:
 registry.register(
     NetworkLink,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="refresh_visibility",
         node_name="refreshVisibility",
         classes=(bool,),
@@ -903,7 +906,7 @@ def __bool__(self) -> bool:
 registry.register(
     NetworkLink,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="fly_to_view",
         node_name="flyToView",
         classes=(bool,),
@@ -915,7 +918,7 @@ def __bool__(self) -> bool:
 registry.register(
     NetworkLink,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="link",
         node_name="Link",
         classes=(Link,),
diff --git a/fastkml/geometry.py b/fastkml/geometry.py
index 3d920a90..bb14f78c 100644
--- a/fastkml/geometry.py
+++ b/fastkml/geometry.py
@@ -69,18 +69,19 @@
 from fastkml.types import Element
 
 __all__ = [
-    "AnyGeometryType",
     "Coordinates",
-    "GeometryType",
+    "InnerBoundaryIs",
     "LineString",
     "LinearRing",
     "MultiGeometry",
-    "MultiGeometryType",
+    "OuterBoundaryIs",
     "Point",
     "Polygon",
+    "create_kml_geometry",
     "create_multigeometry",
 ]
 
+
 logger = logging.getLogger(__name__)
 
 GeometryType = Union[geo.Polygon, geo.LineString, geo.LinearRing, geo.Point]
@@ -299,7 +300,7 @@ def get_tag_name(cls) -> str:
 registry.register(
     Coordinates,
     item=RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         classes=(LineType,),  # type: ignore[arg-type]
         attr_name="coords",
         node_name="coordinates",
@@ -500,7 +501,7 @@ def geometry(self) -> Optional[geo.Point]:
 registry.register(
     Point,
     item=RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         classes=(bool,),
         attr_name="extrude",
         node_name="extrude",
@@ -512,7 +513,7 @@ def geometry(self) -> Optional[geo.Point]:
 registry.register(
     Point,
     item=RegistryItem(
-        ns_ids=("kml", "gx"),
+        ns_ids=("kml", "gx", ""),
         classes=(AltitudeMode,),
         attr_name="altitude_mode",
         node_name="altitudeMode",
@@ -524,7 +525,7 @@ def geometry(self) -> Optional[geo.Point]:
 registry.register(
     Point,
     item=RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         classes=(Coordinates,),
         attr_name="kml_coordinates",
         node_name="coordinates",
@@ -667,7 +668,7 @@ def geometry(self) -> Optional[geo.LineString]:
 registry.register(
     LineString,
     item=RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         classes=(bool,),
         attr_name="extrude",
         node_name="extrude",
@@ -679,7 +680,7 @@ def geometry(self) -> Optional[geo.LineString]:
 registry.register(
     LineString,
     item=RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         classes=(bool,),
         attr_name="tessellate",
         node_name="tessellate",
@@ -691,7 +692,7 @@ def geometry(self) -> Optional[geo.LineString]:
 registry.register(
     LineString,
     item=RegistryItem(
-        ns_ids=("kml", "gx"),
+        ns_ids=("kml", "gx", ""),
         classes=(AltitudeMode,),
         attr_name="altitude_mode",
         node_name="altitudeMode",
@@ -703,7 +704,7 @@ def geometry(self) -> Optional[geo.LineString]:
 registry.register(
     LineString,
     item=RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         classes=(Coordinates,),
         attr_name="kml_coordinates",
         node_name="coordinates",
@@ -935,7 +936,7 @@ def get_tag_name(cls) -> str:
 registry.register(
     BoundaryIs,
     item=RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         classes=(LinearRing,),
         attr_name="kml_geometry",
         node_name="LinearRing",
@@ -1133,7 +1134,7 @@ def __eq__(self, other: object) -> bool:
 registry.register(
     Polygon,
     item=RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         classes=(bool,),
         attr_name="extrude",
         node_name="extrude",
@@ -1145,7 +1146,7 @@ def __eq__(self, other: object) -> bool:
 registry.register(
     Polygon,
     item=RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         classes=(bool,),
         attr_name="tessellate",
         node_name="tessellate",
@@ -1157,7 +1158,7 @@ def __eq__(self, other: object) -> bool:
 registry.register(
     Polygon,
     item=RegistryItem(
-        ns_ids=("kml", "gx"),
+        ns_ids=("kml", "gx", ""),
         classes=(AltitudeMode,),
         attr_name="altitude_mode",
         node_name="altitudeMode",
@@ -1169,7 +1170,7 @@ def __eq__(self, other: object) -> bool:
 registry.register(
     Polygon,
     item=RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         classes=(OuterBoundaryIs,),
         attr_name="outer_boundary",
         node_name="outerBoundaryIs",
@@ -1180,7 +1181,7 @@ def __eq__(self, other: object) -> bool:
 registry.register(
     Polygon,
     item=RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         classes=(InnerBoundaryIs,),
         attr_name="inner_boundaries",
         node_name="innerBoundaryIs",
@@ -1343,7 +1344,7 @@ def geometry(self) -> Optional[MultiGeometryType]:
 registry.register(
     MultiGeometry,
     item=RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         classes=(Point, LineString, Polygon, LinearRing, MultiGeometry),
         attr_name="kml_geometries",
         node_name="(Point|LineString|Polygon|LinearRing|MultiGeometry)",
diff --git a/fastkml/kml.py b/fastkml/kml.py
index 0886c6ba..aee6f9cd 100644
--- a/fastkml/kml.py
+++ b/fastkml/kml.py
@@ -51,6 +51,7 @@
 from fastkml.features import Placemark
 from fastkml.helpers import xml_subelement_list
 from fastkml.helpers import xml_subelement_list_kwarg
+from fastkml.network_link_control import NetworkLinkControl
 from fastkml.overlays import GroundOverlay
 from fastkml.overlays import PhotoOverlay
 from fastkml.registry import RegistryItem
@@ -59,7 +60,14 @@
 
 logger = logging.getLogger(__name__)
 
-kml_children = Union[Folder, Document, Placemark, GroundOverlay, PhotoOverlay]
+kml_children = Union[
+    Folder,
+    Document,
+    Placemark,
+    GroundOverlay,
+    PhotoOverlay,
+    NetworkLinkControl,
+]
 
 
 def lxml_parse_and_validate(
@@ -286,8 +294,19 @@ def write(
     KML,
     RegistryItem(
         ns_ids=("kml",),
-        classes=(Document, Folder, Placemark, GroundOverlay, PhotoOverlay, NetworkLink),
-        node_name="Document,Folder,Placemark,GroundOverlay,PhotoOverlay,NetworkLink",
+        classes=(
+            Document,
+            Folder,
+            Placemark,
+            GroundOverlay,
+            PhotoOverlay,
+            NetworkLink,
+            NetworkLinkControl,
+        ),
+        node_name=(
+            "Document,Folder,Placemark,GroundOverlay,PhotoOverlay,NetworkLink,"
+            "NetworkLinkControl"
+        ),
         attr_name="features",
         get_kwarg=xml_subelement_list_kwarg,
         set_element=xml_subelement_list,
diff --git a/fastkml/links.py b/fastkml/links.py
index a0180bf7..8347f069 100644
--- a/fastkml/links.py
+++ b/fastkml/links.py
@@ -32,6 +32,8 @@
 from fastkml.registry import RegistryItem
 from fastkml.registry import registry
 
+__all__ = ["Icon", "Link"]
+
 
 class Link(_BaseObject):
     """
@@ -124,7 +126,7 @@ def __bool__(self) -> bool:
 registry.register(
     Link,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="href",
         node_name="href",
         classes=(str,),
@@ -135,7 +137,7 @@ def __bool__(self) -> bool:
 registry.register(
     Link,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="refresh_mode",
         node_name="refreshMode",
         classes=(RefreshMode,),
@@ -147,7 +149,7 @@ def __bool__(self) -> bool:
 registry.register(
     Link,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="refresh_interval",
         node_name="refreshInterval",
         classes=(float,),
@@ -159,7 +161,7 @@ def __bool__(self) -> bool:
 registry.register(
     Link,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="view_refresh_mode",
         node_name="viewRefreshMode",
         classes=(ViewRefreshMode,),
@@ -171,7 +173,7 @@ def __bool__(self) -> bool:
 registry.register(
     Link,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="view_refresh_time",
         node_name="viewRefreshTime",
         classes=(float,),
@@ -183,7 +185,7 @@ def __bool__(self) -> bool:
 registry.register(
     Link,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="view_bound_scale",
         node_name="viewBoundScale",
         classes=(float,),
@@ -195,7 +197,7 @@ def __bool__(self) -> bool:
 registry.register(
     Link,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="view_format",
         node_name="viewFormat",
         classes=(str,),
@@ -207,7 +209,7 @@ def __bool__(self) -> bool:
 registry.register(
     Link,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="http_query",
         node_name="httpQuery",
         classes=(str,),
diff --git a/fastkml/model.py b/fastkml/model.py
new file mode 100644
index 00000000..9387b98f
--- /dev/null
+++ b/fastkml/model.py
@@ -0,0 +1,547 @@
+# Copyright (C) 2024 Christian Ledermann
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+"""
+Model element.
+
+The Model element defines a 3D model that is attached to a Placemark.
+
+https://developers.google.com/kml/documentation/models
+https://developers.google.com/kml/documentation/kmlreference#model
+
+"""
+
+from typing import Any
+from typing import Dict
+from typing import Iterable
+from typing import List
+from typing import Optional
+
+from pygeoif.geometry import Point
+
+from fastkml import config
+from fastkml.base import _XMLObject
+from fastkml.enums import AltitudeMode
+from fastkml.helpers import clean_string
+from fastkml.helpers import enum_subelement
+from fastkml.helpers import float_subelement
+from fastkml.helpers import subelement_enum_kwarg
+from fastkml.helpers import subelement_float_kwarg
+from fastkml.helpers import subelement_text_kwarg
+from fastkml.helpers import text_subelement
+from fastkml.helpers import xml_subelement
+from fastkml.helpers import xml_subelement_kwarg
+from fastkml.helpers import xml_subelement_list
+from fastkml.helpers import xml_subelement_list_kwarg
+from fastkml.kml_base import _BaseObject
+from fastkml.links import Link
+from fastkml.registry import RegistryItem
+from fastkml.registry import registry
+
+__all__ = ["Alias", "Location", "Model", "Orientation", "ResourceMap", "Scale"]
+
+
+class Location(_XMLObject):
+    """Represents a location in KML."""
+
+    _default_nsid = config.KML
+
+    latitude: Optional[float]
+    longitude: Optional[float]
+    altitude: Optional[float]
+
+    def __init__(
+        self,
+        ns: Optional[str] = None,
+        name_spaces: Optional[Dict[str, str]] = None,
+        altitude: Optional[float] = None,
+        latitude: Optional[float] = None,
+        longitude: Optional[float] = None,
+        **kwargs: Any,
+    ) -> None:
+        """Create a new Location."""
+        super().__init__(ns=ns, name_spaces=name_spaces, **kwargs)
+        self.altitude = altitude
+        self.latitude = latitude
+        self.longitude = longitude
+
+    def __bool__(self) -> bool:
+        """Return True if latitude and longitude are set."""
+        return all((self.latitude is not None, self.longitude is not None))
+
+    def __repr__(self) -> str:
+        """Create a string (c)representation for Location."""
+        return (
+            f"{self.__class__.__module__}.{self.__class__.__name__}("
+            f"ns={self.ns!r}, "
+            f"name_spaces={self.name_spaces!r}, "
+            f"altitude={self.altitude!r}, "
+            f"latitude={self.latitude!r}, "
+            f"longitude={self.longitude!r}, "
+            f"**{self._get_splat()!r},"
+            ")"
+        )
+
+    @property
+    def geometry(self) -> Optional[Point]:
+        """Return a Point representation of the geometry."""
+        if not self:
+            return None
+        assert self.longitude is not None  # noqa: S101
+        assert self.latitude is not None  # noqa: S101
+        return Point(self.longitude, self.latitude, self.altitude)
+
+
+registry.register(
+    Location,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="longitude",
+        node_name="longitude",
+        classes=(float,),
+        get_kwarg=subelement_float_kwarg,
+        set_element=float_subelement,
+    ),
+)
+registry.register(
+    Location,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="latitude",
+        node_name="latitude",
+        classes=(float,),
+        get_kwarg=subelement_float_kwarg,
+        set_element=float_subelement,
+    ),
+)
+registry.register(
+    Location,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="altitude",
+        node_name="altitude",
+        classes=(float,),
+        get_kwarg=subelement_float_kwarg,
+        set_element=float_subelement,
+        default=0.0,
+    ),
+)
+
+
+class Orientation(_XMLObject):
+    """Represents an orientation in KML."""
+
+    _default_nsid = config.KML
+
+    heading: Optional[float]
+    tilt: Optional[float]
+    roll: Optional[float]
+
+    def __init__(
+        self,
+        ns: Optional[str] = None,
+        name_spaces: Optional[Dict[str, str]] = None,
+        heading: Optional[float] = None,
+        tilt: Optional[float] = None,
+        roll: Optional[float] = None,
+        **kwargs: Any,
+    ) -> None:
+        """Create a new Orientation."""
+        super().__init__(ns=ns, name_spaces=name_spaces, **kwargs)
+        self.heading = heading
+        self.tilt = tilt
+        self.roll = roll
+
+    def __bool__(self) -> bool:
+        """Return True if heading, tilt, or roll are set."""
+        return any(
+            (self.heading is not None, self.tilt is not None, self.roll is not None),
+        )
+
+    def __repr__(self) -> str:
+        """Create a string (c)representation for Orientation."""
+        return (
+            f"{self.__class__.__module__}.{self.__class__.__name__}("
+            f"ns={self.ns!r}, "
+            f"name_spaces={self.name_spaces!r}, "
+            f"heading={self.heading!r}, "
+            f"tilt={self.tilt!r}, "
+            f"roll={self.roll!r}, "
+            f"**{self._get_splat()!r},"
+            ")"
+        )
+
+
+registry.register(
+    Orientation,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="heading",
+        node_name="heading",
+        classes=(float,),
+        get_kwarg=subelement_float_kwarg,
+        set_element=float_subelement,
+        default=0.0,
+    ),
+)
+registry.register(
+    Orientation,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="tilt",
+        node_name="tilt",
+        classes=(float,),
+        get_kwarg=subelement_float_kwarg,
+        set_element=float_subelement,
+        default=0.0,
+    ),
+)
+registry.register(
+    Orientation,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="roll",
+        node_name="roll",
+        classes=(float,),
+        get_kwarg=subelement_float_kwarg,
+        set_element=float_subelement,
+        default=0.0,
+    ),
+)
+
+
+class Scale(_XMLObject):
+    """Represents a scale in KML."""
+
+    _default_nsid = config.KML
+
+    x: Optional[float]
+    y: Optional[float]
+    z: Optional[float]
+
+    def __init__(
+        self,
+        ns: Optional[str] = None,
+        name_spaces: Optional[Dict[str, str]] = None,
+        x: Optional[float] = None,
+        y: Optional[float] = None,
+        z: Optional[float] = None,
+        **kwargs: Any,
+    ) -> None:
+        """Create a new Scale."""
+        super().__init__(ns=ns, name_spaces=name_spaces, **kwargs)
+        self.x = x
+        self.y = y
+        self.z = z
+
+    def __bool__(self) -> bool:
+        """Return True if x, y, or z are set."""
+        return any((self.x is not None, self.y is not None, self.z is not None))
+
+    def __repr__(self) -> str:
+        """Create a string (c)representation for Scale."""
+        return (
+            f"{self.__class__.__module__}.{self.__class__.__name__}("
+            f"ns={self.ns!r}, "
+            f"name_spaces={self.name_spaces!r}, "
+            f"x={self.x!r}, "
+            f"y={self.y!r}, "
+            f"z={self.z!r}, "
+            f"**{self._get_splat()!r},"
+            ")"
+        )
+
+
+registry.register(
+    Scale,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="x",
+        node_name="x",
+        classes=(float,),
+        get_kwarg=subelement_float_kwarg,
+        set_element=float_subelement,
+        default=1.0,
+    ),
+)
+registry.register(
+    Scale,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="y",
+        node_name="y",
+        classes=(float,),
+        get_kwarg=subelement_float_kwarg,
+        set_element=float_subelement,
+        default=1.0,
+    ),
+)
+registry.register(
+    Scale,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="z",
+        node_name="z",
+        classes=(float,),
+        get_kwarg=subelement_float_kwarg,
+        set_element=float_subelement,
+        default=1.0,
+    ),
+)
+
+
+class Alias(_XMLObject):
+    """Represents an alias in KML."""
+
+    _default_nsid = config.KML
+
+    target_href: Optional[str]
+    source_href: Optional[str]
+
+    def __init__(
+        self,
+        ns: Optional[str] = None,
+        name_spaces: Optional[Dict[str, str]] = None,
+        target_href: Optional[str] = None,
+        source_href: Optional[str] = None,
+        **kwargs: Any,
+    ) -> None:
+        """Create a new Alias."""
+        super().__init__(ns=ns, name_spaces=name_spaces, **kwargs)
+        self.target_href = clean_string(target_href)
+        self.source_href = clean_string(source_href)
+
+    def __bool__(self) -> bool:
+        """Return True if target_href or source_href are set."""
+        return any((self.target_href is not None, self.source_href is not None))
+
+    def __repr__(self) -> str:
+        """Create a string (c)representation for Alias."""
+        return (
+            f"{self.__class__.__module__}.{self.__class__.__name__}("
+            f"ns={self.ns!r}, "
+            f"name_spaces={self.name_spaces!r}, "
+            f"target_href={self.target_href!r}, "
+            f"source_href={self.source_href!r}, "
+            f"**{self._get_splat()!r},"
+            ")"
+        )
+
+
+registry.register(
+    Alias,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="target_href",
+        node_name="targetHref",
+        classes=(str,),
+        get_kwarg=subelement_text_kwarg,
+        set_element=text_subelement,
+    ),
+)
+registry.register(
+    Alias,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="source_href",
+        node_name="sourceHref",
+        classes=(str,),
+        get_kwarg=subelement_text_kwarg,
+        set_element=text_subelement,
+    ),
+)
+
+
+class ResourceMap(_XMLObject):
+    """Represents a resource map in KML."""
+
+    _default_nsid = config.KML
+
+    aliases: List[Alias]
+
+    def __init__(
+        self,
+        ns: Optional[str] = None,
+        name_spaces: Optional[Dict[str, str]] = None,
+        aliases: Optional[Iterable[Alias]] = None,
+        **kwargs: Any,
+    ) -> None:
+        """Create a new ResourceMap."""
+        super().__init__(ns=ns, name_spaces=name_spaces, **kwargs)
+        self.aliases = list(aliases) if aliases is not None else []
+
+    def __bool__(self) -> bool:
+        """Return True if aliases are set."""
+        return bool(self.aliases)
+
+    def __repr__(self) -> str:
+        """Create a string (c)representation for ResourceMap."""
+        return (
+            f"{self.__class__.__module__}.{self.__class__.__name__}("
+            f"ns={self.ns!r}, "
+            f"name_spaces={self.name_spaces!r}, "
+            f"aliases={self.aliases!r}, "
+            f"**{self._get_splat()!r},"
+            ")"
+        )
+
+
+registry.register(
+    ResourceMap,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="aliases",
+        node_name="Alias",
+        classes=(Alias,),
+        get_kwarg=xml_subelement_list_kwarg,
+        set_element=xml_subelement_list,
+    ),
+)
+
+
+class Model(_BaseObject):
+    """Represents a model in KML."""
+
+    altitude_mode: Optional[AltitudeMode]
+    location: Optional[Location]
+    orientation: Optional[Orientation]
+    scale: Optional[Scale]
+    link: Optional[Link]
+    resource_map: Optional[ResourceMap]
+
+    def __init__(
+        self,
+        ns: Optional[str] = None,
+        name_spaces: Optional[Dict[str, str]] = None,
+        id: Optional[str] = None,
+        target_id: Optional[str] = None,
+        altitude_mode: Optional[AltitudeMode] = None,
+        location: Optional[Location] = None,
+        orientation: Optional[Orientation] = None,
+        scale: Optional[Scale] = None,
+        link: Optional[Link] = None,
+        resource_map: Optional[ResourceMap] = None,
+        **kwargs: Any,
+    ) -> None:
+        """Create a new Model."""
+        super().__init__(
+            ns=ns,
+            name_spaces=name_spaces,
+            id=id,
+            target_id=target_id,
+            **kwargs,
+        )
+        self.altitude_mode = altitude_mode
+        self.location = location
+        self.orientation = orientation
+        self.scale = scale
+        self.link = link
+        self.resource_map = resource_map
+
+    def __bool__(self) -> bool:
+        """Return True if link and location are set."""
+        return all((self.link, self.location))
+
+    def __repr__(self) -> str:
+        """Create a string representation for Model."""
+        return (
+            f"{self.__class__.__module__}.{self.__class__.__name__}("
+            f"ns={self.ns!r}, "
+            f"name_spaces={self.name_spaces!r}, "
+            f"id={self.id!r}, "
+            f"target_id={self.target_id!r}, "
+            f"altitude_mode={self.altitude_mode}, "
+            f"location={self.location!r}, "
+            f"orientation={self.orientation!r}, "
+            f"scale={self.scale!r}, "
+            f"link={self.link!r}, "
+            f"resource_map={self.resource_map!r}, "
+            f"**{self._get_splat()!r},"
+            ")"
+        )
+
+    @property
+    def geometry(self) -> Optional[Point]:
+        """Return a Point representation of the geometry."""
+        return self.location.geometry if self.location else None
+
+
+registry.register(
+    Model,
+    RegistryItem(
+        ns_ids=("kml", "gx", ""),
+        attr_name="altitude_mode",
+        node_name="altitudeMode",
+        classes=(AltitudeMode,),
+        get_kwarg=subelement_enum_kwarg,
+        set_element=enum_subelement,
+        default=AltitudeMode.clamp_to_ground,
+    ),
+)
+registry.register(
+    Model,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="location",
+        node_name="Location",
+        classes=(Location,),
+        get_kwarg=xml_subelement_kwarg,
+        set_element=xml_subelement,
+    ),
+)
+registry.register(
+    Model,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="orientation",
+        node_name="Orientation",
+        classes=(Orientation,),
+        get_kwarg=xml_subelement_kwarg,
+        set_element=xml_subelement,
+    ),
+)
+registry.register(
+    Model,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="scale",
+        node_name="Scale",
+        classes=(Scale,),
+        get_kwarg=xml_subelement_kwarg,
+        set_element=xml_subelement,
+    ),
+)
+registry.register(
+    Model,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="link",
+        node_name="Link",
+        classes=(Link,),
+        get_kwarg=xml_subelement_kwarg,
+        set_element=xml_subelement,
+    ),
+)
+registry.register(
+    Model,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="resource_map",
+        node_name="ResourceMap",
+        classes=(ResourceMap,),
+        get_kwarg=xml_subelement_kwarg,
+        set_element=xml_subelement,
+    ),
+)
diff --git a/fastkml/network_link_control.py b/fastkml/network_link_control.py
new file mode 100644
index 00000000..01d2d066
--- /dev/null
+++ b/fastkml/network_link_control.py
@@ -0,0 +1,261 @@
+# Copyright (C) 2024 Christian Ledermann
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+"""
+NetworkLinkControl class.
+
+Controls the behavior of files fetched by a <NetworkLink>.
+
+https://developers.google.com/kml/documentation/kmlreference#networklinkcontrol
+"""
+
+import logging
+from typing import Any
+from typing import Dict
+from typing import Optional
+from typing import Union
+
+from fastkml import config
+from fastkml.base import _XMLObject
+from fastkml.helpers import clean_string
+from fastkml.helpers import datetime_subelement
+from fastkml.helpers import datetime_subelement_kwarg
+from fastkml.helpers import float_subelement
+from fastkml.helpers import subelement_float_kwarg
+from fastkml.helpers import subelement_text_kwarg
+from fastkml.helpers import text_subelement
+from fastkml.helpers import xml_subelement
+from fastkml.helpers import xml_subelement_kwarg
+from fastkml.registry import RegistryItem
+from fastkml.registry import registry
+from fastkml.times import KmlDateTime
+from fastkml.views import Camera
+from fastkml.views import LookAt
+
+__all__ = [
+    "NetworkLinkControl",
+]
+
+logger = logging.getLogger(__name__)
+
+
+class NetworkLinkControl(_XMLObject):
+    """Controls the behavior of files fetched by a <NetworkLink>."""
+
+    _default_nsid = config.KML
+
+    min_refresh_period: Optional[float]
+    max_session_length: Optional[float]
+    cookie: Optional[str]
+    message: Optional[str]
+    link_name: Optional[str]
+    link_description: Optional[str]
+    link_snippet: Optional[str]
+    expires: Optional[KmlDateTime]
+    view: Union[Camera, LookAt, None]
+
+    def __init__(
+        self,
+        ns: Optional[str] = None,
+        name_spaces: Optional[Dict[str, str]] = None,
+        min_refresh_period: Optional[float] = None,
+        max_session_length: Optional[float] = None,
+        cookie: Optional[str] = None,
+        message: Optional[str] = None,
+        link_name: Optional[str] = None,
+        link_description: Optional[str] = None,
+        link_snippet: Optional[str] = None,
+        expires: Optional[KmlDateTime] = None,
+        view: Optional[Union[Camera, LookAt]] = None,
+        **kwargs: Any,
+    ) -> None:
+        """
+        Create a NetworkLinkControl object.
+
+        Parameters
+        ----------
+        ns : str, optional
+            The namespace to use for the NetworkLinkControl object.
+        name_spaces : dict, optional
+            A dictionary of namespaces to use for the NetworkLinkControl object.
+        min_refresh_period : float, optional
+            The minimum number of seconds between fetches. A value of -1 indicates that
+            the NetworkLinkControl object should be fetched only once.
+        max_session_length : float, optional
+            The maximum number of seconds that the link should be followed.
+        cookie : str, optional
+            A string value that can be used to identify the client request.
+        message : str, optional
+            A message to be displayed to the user in case of a failure.
+        link_name : str, optional
+            The name of the link.
+        link_description : str, optional
+            A description of the link.
+        link_snippet : str, optional
+            A snippet of text to be displayed in the link.
+        expires : KmlDateTime, optional
+            The time at which the link should expire.
+        view : Camera or LookAt, optional
+            The view to be used when the link is followed.
+        **kwargs : Any, optional
+            Additional keyword arguments.
+
+        """
+        super().__init__(
+            ns=ns,
+            name_spaces=name_spaces,
+            **kwargs,
+        )
+        self.min_refresh_period = min_refresh_period
+        self.max_session_length = max_session_length
+        self.cookie = clean_string(cookie)
+        self.message = clean_string(message)
+        self.link_name = clean_string(link_name)
+        self.link_description = clean_string(link_description)
+        self.link_snippet = clean_string(link_snippet)
+        self.expires = expires
+        self.view = view
+
+    def __repr__(self) -> str:
+        """
+        Return a string representation of the NetworkLinkControl object.
+
+        Returns
+        -------
+            str: A string representation of the NetworkLinkControl object.
+
+        """
+        return (
+            f"{self.__class__.__module__}.{self.__class__.__name__}("
+            f"ns={self.ns!r}, "
+            f"name_spaces={self.name_spaces!r}, "
+            f"min_refresh_period={self.min_refresh_period!r}, "
+            f"max_session_length={self.max_session_length!r}, "
+            f"cookie={self.cookie!r}, "
+            f"message={self.message!r}, "
+            f"link_name={self.link_name!r}, "
+            f"link_description={self.link_description!r}, "
+            f"link_snippet={self.link_snippet!r}, "
+            f"expires={self.expires!r}, "
+            f"view={self.view!r}, "
+            f"**{self._get_splat()!r},"
+            ")"
+        )
+
+
+registry.register(
+    NetworkLinkControl,
+    RegistryItem(
+        ns_ids=("kml",),
+        attr_name="min_refresh_period",
+        node_name="minRefreshPeriod",
+        classes=(float,),
+        get_kwarg=subelement_float_kwarg,
+        set_element=float_subelement,
+        default=0,
+    ),
+)
+registry.register(
+    NetworkLinkControl,
+    RegistryItem(
+        ns_ids=("kml",),
+        attr_name="max_session_length",
+        node_name="maxSessionLength",
+        classes=(float,),
+        get_kwarg=subelement_float_kwarg,
+        set_element=float_subelement,
+        default=-1,
+    ),
+)
+registry.register(
+    NetworkLinkControl,
+    RegistryItem(
+        ns_ids=("kml",),
+        attr_name="cookie",
+        node_name="cookie",
+        classes=(str,),
+        get_kwarg=subelement_text_kwarg,
+        set_element=text_subelement,
+    ),
+)
+registry.register(
+    NetworkLinkControl,
+    RegistryItem(
+        ns_ids=("kml",),
+        attr_name="message",
+        node_name="message",
+        classes=(str,),
+        get_kwarg=subelement_text_kwarg,
+        set_element=text_subelement,
+    ),
+)
+registry.register(
+    NetworkLinkControl,
+    RegistryItem(
+        ns_ids=("kml",),
+        attr_name="link_name",
+        node_name="linkName",
+        classes=(str,),
+        get_kwarg=subelement_text_kwarg,
+        set_element=text_subelement,
+    ),
+)
+registry.register(
+    NetworkLinkControl,
+    RegistryItem(
+        ns_ids=("kml",),
+        attr_name="link_description",
+        node_name="linkDescription",
+        classes=(str,),
+        get_kwarg=subelement_text_kwarg,
+        set_element=text_subelement,
+    ),
+)
+registry.register(
+    NetworkLinkControl,
+    RegistryItem(
+        ns_ids=("kml",),
+        attr_name="link_snippet",
+        node_name="linkSnippet",
+        classes=(str,),
+        get_kwarg=subelement_text_kwarg,
+        set_element=text_subelement,
+    ),
+)
+registry.register(
+    NetworkLinkControl,
+    item=RegistryItem(
+        ns_ids=("kml",),
+        classes=(KmlDateTime,),
+        attr_name="expires",
+        node_name="expires",
+        get_kwarg=datetime_subelement_kwarg,
+        set_element=datetime_subelement,
+    ),
+)
+registry.register(
+    NetworkLinkControl,
+    RegistryItem(
+        ns_ids=("kml",),
+        attr_name="view",
+        node_name="Camera,LookAt",
+        classes=(
+            Camera,
+            LookAt,
+        ),
+        get_kwarg=xml_subelement_kwarg,
+        set_element=xml_subelement,
+    ),
+)
diff --git a/fastkml/overlays.py b/fastkml/overlays.py
index ab6a9551..48bfcdd9 100644
--- a/fastkml/overlays.py
+++ b/fastkml/overlays.py
@@ -30,6 +30,7 @@
 from fastkml.enums import AltitudeMode
 from fastkml.enums import GridOrigin
 from fastkml.enums import Shape
+from fastkml.enums import Units
 from fastkml.features import Snippet
 from fastkml.features import _Feature
 from fastkml.geometry import LinearRing
@@ -37,8 +38,12 @@
 from fastkml.geometry import MultiGeometry
 from fastkml.geometry import Point
 from fastkml.geometry import Polygon
+from fastkml.helpers import attribute_enum_kwarg
+from fastkml.helpers import attribute_float_kwarg
 from fastkml.helpers import clean_string
+from fastkml.helpers import enum_attribute
 from fastkml.helpers import enum_subelement
+from fastkml.helpers import float_attribute
 from fastkml.helpers import float_subelement
 from fastkml.helpers import int_subelement
 from fastkml.helpers import subelement_enum_kwarg
@@ -63,9 +68,13 @@
 __all__ = [
     "GroundOverlay",
     "ImagePyramid",
-    "KmlGeometry",
     "LatLonBox",
+    "OverlayXY",
     "PhotoOverlay",
+    "RotationXY",
+    "ScreenOverlay",
+    "ScreenXY",
+    "Size",
     "ViewVolume",
 ]
 
@@ -220,7 +229,7 @@ def __init__(
 registry.register(
     _Overlay,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="color",
         node_name="color",
         classes=(str,),
@@ -232,7 +241,7 @@ def __init__(
 registry.register(
     _Overlay,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="draw_order",
         node_name="drawOrder",
         classes=(int,),
@@ -244,7 +253,7 @@ def __init__(
 registry.register(
     _Overlay,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="icon",
         node_name="Icon",
         classes=(Icon,),
@@ -371,7 +380,7 @@ def __bool__(self) -> bool:
 registry.register(
     ViewVolume,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="left_fov",
         node_name="leftFov",
         classes=(float,),
@@ -383,7 +392,7 @@ def __bool__(self) -> bool:
 registry.register(
     ViewVolume,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="right_fov",
         node_name="rightFov",
         classes=(float,),
@@ -395,7 +404,7 @@ def __bool__(self) -> bool:
 registry.register(
     ViewVolume,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="bottom_fov",
         node_name="bottomFov",
         classes=(float,),
@@ -407,7 +416,7 @@ def __bool__(self) -> bool:
 registry.register(
     ViewVolume,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="top_fov",
         node_name="topFov",
         classes=(float,),
@@ -419,7 +428,7 @@ def __bool__(self) -> bool:
 registry.register(
     ViewVolume,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="near",
         node_name="near",
         classes=(float,),
@@ -541,7 +550,7 @@ def __bool__(self) -> bool:
 registry.register(
     ImagePyramid,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="tile_size",
         node_name="tileSize",
         classes=(int,),
@@ -553,7 +562,7 @@ def __bool__(self) -> bool:
 registry.register(
     ImagePyramid,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="max_width",
         node_name="maxWidth",
         classes=(int,),
@@ -564,7 +573,7 @@ def __bool__(self) -> bool:
 registry.register(
     ImagePyramid,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="max_height",
         node_name="maxHeight",
         classes=(int,),
@@ -575,7 +584,7 @@ def __bool__(self) -> bool:
 registry.register(
     ImagePyramid,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="grid_origin",
         node_name="gridOrigin",
         classes=(GridOrigin,),
@@ -812,7 +821,7 @@ def __repr__(self) -> str:
 registry.register(
     PhotoOverlay,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="rotation",
         node_name="rotation",
         classes=(float,),
@@ -824,7 +833,7 @@ def __repr__(self) -> str:
 registry.register(
     PhotoOverlay,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="view_volume",
         node_name="ViewVolume",
         classes=(ViewVolume,),
@@ -835,7 +844,7 @@ def __repr__(self) -> str:
 registry.register(
     PhotoOverlay,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="image_pyramid",
         node_name="ImagePyramid",
         classes=(ImagePyramid,),
@@ -846,7 +855,7 @@ def __repr__(self) -> str:
 registry.register(
     PhotoOverlay,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="point",
         node_name="Point",
         classes=(Point,),
@@ -857,7 +866,7 @@ def __repr__(self) -> str:
 registry.register(
     PhotoOverlay,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="shape",
         node_name="shape",
         classes=(Shape,),
@@ -983,7 +992,7 @@ def __bool__(self) -> bool:
 registry.register(
     LatLonBox,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="north",
         node_name="north",
         classes=(float,),
@@ -994,7 +1003,7 @@ def __bool__(self) -> bool:
 registry.register(
     LatLonBox,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="south",
         node_name="south",
         classes=(float,),
@@ -1005,7 +1014,7 @@ def __bool__(self) -> bool:
 registry.register(
     LatLonBox,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="east",
         node_name="east",
         classes=(float,),
@@ -1016,7 +1025,7 @@ def __bool__(self) -> bool:
 registry.register(
     LatLonBox,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="west",
         node_name="west",
         classes=(float,),
@@ -1027,7 +1036,7 @@ def __bool__(self) -> bool:
 registry.register(
     LatLonBox,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="rotation",
         node_name="rotation",
         classes=(float,),
@@ -1232,7 +1241,7 @@ def __repr__(self) -> str:
 registry.register(
     GroundOverlay,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="altitude",
         node_name="altitude",
         classes=(float,),
@@ -1256,7 +1265,7 @@ def __repr__(self) -> str:
 registry.register(
     GroundOverlay,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="lat_lon_box",
         node_name="LatLonBox",
         classes=(LatLonBox,),
@@ -1264,3 +1273,405 @@ def __repr__(self) -> str:
         set_element=xml_subelement,
     ),
 )
+
+
+class _XY(_XMLObject):
+    """Specifies a point relative to the screen origin in pixels."""
+
+    _default_nsid = config.KML
+
+    x: Optional[float]
+    y: Optional[float]
+    x_units: Optional[Units]
+
+    y_units: Optional[Units]
+
+    def __init__(
+        self,
+        ns: Optional[str] = None,
+        name_spaces: Optional[Dict[str, str]] = None,
+        x: Optional[float] = None,
+        y: Optional[float] = None,
+        x_units: Optional[Units] = None,
+        y_units: Optional[Units] = None,
+        **kwargs: Any,
+    ) -> None:
+        """
+        Initialize a new _XY object.
+
+        Args:
+        ----
+        ns : Optional[str]
+            The namespace for the element.
+        name_spaces : Optional[Dict[str, str]]
+            A dictionary of namespace prefixes and URIs.
+        x : Optional[float]
+            The horizontal position of the point relative to the left edge.
+        y : Optional[float]
+            The vertical position of the point relative to the bottom edge.
+        x_units : Optional[Units]
+            The horizontal units of the point.
+        y_units : Optional[Units]
+            The vertical units of the point
+        kwargs : Any
+            Additional keyword arguments.
+
+        """
+        super().__init__(ns=ns, name_spaces=name_spaces, **kwargs)
+        self.x = x
+        self.y = y
+        self.x_units = x_units
+        self.y_units = y_units
+
+    def __repr__(self) -> str:
+        """Create a string (c)representation for _XY."""
+        return (
+            f"{self.__class__.__module__}.{self.__class__.__name__}("
+            f"ns={self.ns!r}, "
+            f"name_spaces={self.name_spaces!r}, "
+            f"x={self.x!r}, "
+            f"y={self.y!r}, "
+            f"x_units={self.x_units}, "
+            f"y_units={self.y_units}, "
+            f"**{self._get_splat()!r},"
+            ")"
+        )
+
+    def __bool__(self) -> bool:
+        """
+        Check if all the attributes necessary are not None.
+
+        Returns
+        -------
+            bool: True if all attributes (x, y) are not None.
+
+        """
+        return all([self.x is not None, self.y is not None])
+
+
+registry.register(
+    _XY,
+    RegistryItem(
+        ns_ids=("", "kml"),
+        attr_name="x",
+        node_name="x",
+        classes=(float,),
+        get_kwarg=attribute_float_kwarg,
+        set_element=float_attribute,
+    ),
+)
+registry.register(
+    _XY,
+    RegistryItem(
+        ns_ids=("", "kml"),
+        attr_name="y",
+        node_name="y",
+        classes=(float,),
+        get_kwarg=attribute_float_kwarg,
+        set_element=float_attribute,
+    ),
+)
+registry.register(
+    _XY,
+    RegistryItem(
+        ns_ids=("", "kml"),
+        attr_name="x_units",
+        node_name="xunits",
+        classes=(Units,),
+        get_kwarg=attribute_enum_kwarg,
+        set_element=enum_attribute,
+        default=Units.fraction,
+    ),
+)
+registry.register(
+    _XY,
+    RegistryItem(
+        ns_ids=("", "kml"),
+        attr_name="y_units",
+        node_name="yunits",
+        classes=(Units,),
+        get_kwarg=attribute_enum_kwarg,
+        set_element=enum_attribute,
+        default=Units.fraction,
+    ),
+)
+
+
+class OverlayXY(_XY):
+    """Specifies the placement of the overlay on the screen."""
+
+    @classmethod
+    def get_tag_name(cls) -> str:
+        """Return the tag name."""
+        return "overlayXY"
+
+
+class ScreenXY(_XY):
+    """Specifies the placement of the overlay on the screen."""
+
+    @classmethod
+    def get_tag_name(cls) -> str:
+        """Return the tag name."""
+        return "screenXY"
+
+
+class RotationXY(_XY):
+    """Specifies the rotation of the overlay on the screen."""
+
+    @classmethod
+    def get_tag_name(cls) -> str:
+        """Return the tag name."""
+        return "rotationXY"
+
+
+class Size(_XY):
+    """Specifies the size of the overlay on the screen."""
+
+    @classmethod
+    def get_tag_name(cls) -> str:
+        """Return the tag name."""
+        return "size"
+
+
+class ScreenOverlay(_Overlay):
+    """
+    A ScreenOverlay draws an image overlay fixed to the screen.
+
+    This element draws an image overlay fixed to the screen. Sample uses include
+    watermarking the map with an image, such as a company logo, or adding a
+    heads-up display (HUD) to show real-time information.
+
+    The <href> child of <Icon> specifies the image to be used as the overlay.
+    This file can be either on a local file system or on a web server.
+
+    https://developers.google.com/kml/documentation/kmlreference#screenoverlay
+    """
+
+    def __init__(
+        self,
+        ns: Optional[str] = None,
+        name_spaces: Optional[Dict[str, str]] = None,
+        id: Optional[str] = None,
+        target_id: Optional[str] = None,
+        name: Optional[str] = None,
+        visibility: Optional[bool] = None,
+        isopen: Optional[bool] = None,
+        atom_link: Optional[atom.Link] = None,
+        atom_author: Optional[atom.Author] = None,
+        address: Optional[str] = None,
+        phone_number: Optional[str] = None,
+        snippet: Optional[Snippet] = None,
+        description: Optional[str] = None,
+        view: Optional[Union[Camera, LookAt]] = None,
+        times: Optional[Union[TimeSpan, TimeStamp]] = None,
+        style_url: Optional[StyleUrl] = None,
+        styles: Optional[Iterable[Union[Style, StyleMap]]] = None,
+        region: Optional[Region] = None,
+        extended_data: Optional[ExtendedData] = None,
+        color: Optional[str] = None,
+        draw_order: Optional[int] = None,
+        icon: Optional[Icon] = None,
+        # Screen Overlay specific
+        overlay_xy: Optional[OverlayXY] = None,
+        screen_xy: Optional[ScreenXY] = None,
+        rotation_xy: Optional[RotationXY] = None,
+        size: Optional[Size] = None,
+        rotation: Optional[float] = None,
+        **kwargs: Any,
+    ) -> None:
+        """
+        Initialize a new ScreenOverlay object.
+
+        Args:
+        ----
+        ns : Optional[str]
+            The namespace of the element.
+        name_spaces : Optional[Dict[str, str]]
+            The dictionary of namespace prefixes and URIs.
+        id : Optional[str]
+            The ID of the element.
+        target_id : Optional[str]
+            The target ID of the element.
+        name : Optional[str]
+            The name of the element.
+        visibility : Optional[bool]
+            The visibility of the element.
+        isopen : Optional[bool]
+            The open state of the element.
+        atom_link : Optional[atom.Link]
+            The Atom link associated with the element.
+        atom_author : Optional[atom.Author]
+            The Atom author associated with the element.
+        address : Optional[str]
+            The address of the element.
+        phone_number : Optional[str]
+            The phone number of the element.
+        snippet : Optional[Snippet]
+            The snippet associated with the element.
+        description : Optional[str]
+            The description of the element.
+        view : Optional[Union[Camera, LookAt]]
+            The view associated with the element.
+        times : Optional[Union[TimeSpan, TimeStamp]]
+            The times associated with the element.
+        style_url : Optional[StyleUrl]
+            The style URL of the element.
+        styles : Optional[Iterable[Union[Style, StyleMap]]]
+            The styles associated with the element.
+        region : Optional[Region]
+            The region associated with the element.
+        extended_data : Optional[ExtendedData]
+            The extended data associated with the element.
+        color : Optional[str]
+            The color of the element.
+        draw_order : Optional[int]
+            The draw order of the element.
+        icon : Optional[Icon]
+            The icon associated with the element.
+        altitude : Optional[float]
+            The altitude of the element.
+        altitude_mode : Optional[AltitudeMode]
+            The altitude mode of the element.
+        lat_lon_box : Optional[LatLonBox]
+            The latitude-longitude box associated with the element.
+        overlay_xy : Optional[OverlayXY]
+            The overlay XY associated with the element.
+        screen_xy : Optional[ScreenXY]
+            The screen XY associated with the element.
+        rotation_xy : Optional[RotationXY]
+            The rotation XY associated with the element.
+        size : Optional[Size]
+            The size associated with the element.
+        rotation : Optional[float]
+            The rotation of the element.
+        kwargs : Any
+            Additional keyword arguments.
+
+        Returns:
+        -------
+        None
+
+        """
+        super().__init__(
+            ns=ns,
+            name_spaces=name_spaces,
+            id=id,
+            target_id=target_id,
+            name=name,
+            visibility=visibility,
+            isopen=isopen,
+            atom_link=atom_link,
+            atom_author=atom_author,
+            address=address,
+            phone_number=phone_number,
+            snippet=snippet,
+            description=description,
+            view=view,
+            times=times,
+            style_url=style_url,
+            styles=styles,
+            region=region,
+            extended_data=extended_data,
+            color=color,
+            draw_order=draw_order,
+            icon=icon,
+            **kwargs,
+        )
+        self.overlay_xy = overlay_xy
+        self.screen_xy = screen_xy
+        self.rotation_xy = rotation_xy
+        self.size = size
+        self.rotation = rotation
+
+    def __repr__(self) -> str:
+        """Create a string (c)representation for ScreenOverlay."""
+        return (
+            f"{self.__class__.__module__}.{self.__class__.__name__}("
+            f"ns={self.ns!r}, "
+            f"name_spaces={self.name_spaces!r}, "
+            f"id={self.id!r}, "
+            f"target_id={self.target_id!r}, "
+            f"name={self.name!r}, "
+            f"visibility={self.visibility!r}, "
+            f"isopen={self.isopen!r}, "
+            f"atom_link={self.atom_link!r}, "
+            f"atom_author={self.atom_author!r}, "
+            f"address={self.address!r}, "
+            f"phone_number={self.phone_number!r}, "
+            f"snippet={self.snippet!r}, "
+            f"description={self.description!r}, "
+            f"view={self.view!r}, "
+            f"times={self.times!r}, "
+            f"style_url={self.style_url!r}, "
+            f"styles={self.styles!r}, "
+            f"region={self.region!r}, "
+            f"extended_data={self.extended_data!r}, "
+            f"color={self.color!r}, "
+            f"draw_order={self.draw_order!r}, "
+            f"icon={self.icon!r}, "
+            f"overlay_xy={self.overlay_xy!r}, "
+            f"screen_xy={self.screen_xy!r}, "
+            f"rotation_xy={self.rotation_xy!r}, "
+            f"size={self.size!r}, "
+            f"rotation={self.rotation!r}, "
+            f"**{self._get_splat()!r},"
+            ")"
+        )
+
+
+registry.register(
+    ScreenOverlay,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="overlay_xy",
+        node_name="overlayXY",
+        classes=(OverlayXY,),
+        get_kwarg=xml_subelement_kwarg,
+        set_element=xml_subelement,
+    ),
+)
+registry.register(
+    ScreenOverlay,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="screen_xy",
+        node_name="screenXY",
+        classes=(ScreenXY,),
+        get_kwarg=xml_subelement_kwarg,
+        set_element=xml_subelement,
+    ),
+)
+registry.register(
+    ScreenOverlay,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="rotation_xy",
+        node_name="rotationXY",
+        classes=(RotationXY,),
+        get_kwarg=xml_subelement_kwarg,
+        set_element=xml_subelement,
+    ),
+)
+registry.register(
+    ScreenOverlay,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="size",
+        node_name="size",
+        classes=(Size,),
+        get_kwarg=xml_subelement_kwarg,
+        set_element=xml_subelement,
+    ),
+)
+registry.register(
+    ScreenOverlay,
+    RegistryItem(
+        ns_ids=("kml", ""),
+        attr_name="rotation",
+        node_name="rotation",
+        classes=(float,),
+        get_kwarg=subelement_float_kwarg,
+        set_element=float_subelement,
+        default=0.0,
+    ),
+)
diff --git a/fastkml/registry.py b/fastkml/registry.py
index 96e53bb1..d603a80b 100644
--- a/fastkml/registry.py
+++ b/fastkml/registry.py
@@ -20,14 +20,6 @@
 This approach allows for flexible, declarative mapping between XML and Python objects,
 with the registry acting as a central configuration for these mappings.
 
-Direct ``Registry`` class use is typically only for library internals or advanced
-customization. For normal usage, stick with the ``registry`` instance:
-
-- The library is designed around this global instance.
-- Ensures all parts of the library use the same registry.
-- Pre-populated with standard KML mappings.
-- Singleton pattern: Avoids multiple conflicting registries.
-
 """
 
 from dataclasses import dataclass
@@ -89,6 +81,7 @@ class RegistryItem:
     - ``attr_name``: The name of the attribute on the Python object that corresponds to
       the XML object.
     - ``get_kwarg``: A function that retrieves keyword arguments for the Python object.
+    - ``set_element``: A function that sets the XML element for the Python object.
     - ``type``: The type of the XML object.
     - ``node_name``: The name of the XML node that the mapping applies to.
     - ``default``: An optional default value for the Python object attribute.
@@ -187,5 +180,18 @@ def get(self, cls: Type["_XMLObject"]) -> List[RegistryItem]:
 
 
 registry = Registry()
+"""
+Global Registry.
+
+You should use ``registry.registry`` instead of instantiating ``registry.Registry()``
+because ``registry.registry`` is a pre-instantiated global instance, ensuring a single,
+shared registry across the entire application.
+It is pre-populated with all the necessary KML element registrations.
+If you need to add custom elements, you can extend the existing registry without
+creating a new one.
+Using the pre-defined ``registry.registry`` ensures you're working with the complete,
+properly initialized registry for the fastkml library.
+
+"""
 
-__all__ = ["registry", "RegistryItem"]
+__all__ = ["RegistryItem", "registry"]
diff --git a/fastkml/styles.py b/fastkml/styles.py
index 671a7943..b5b9292a 100644
--- a/fastkml/styles.py
+++ b/fastkml/styles.py
@@ -62,6 +62,19 @@
 
 logger = logging.getLogger(__name__)
 
+__all__ = [
+    "BalloonStyle",
+    "HotSpot",
+    "IconStyle",
+    "LabelStyle",
+    "LineStyle",
+    "Pair",
+    "PolyStyle",
+    "Style",
+    "StyleMap",
+    "StyleUrl",
+]
+
 
 class StyleUrl(_XMLObject):
     """
@@ -143,7 +156,7 @@ def get_tag_name(cls) -> str:
 registry.register(
     StyleUrl,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="url",
         node_name="styleUrl",
         classes=(str,),
@@ -229,7 +242,7 @@ def __init__(
 registry.register(
     _ColorStyle,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="color",
         node_name="color",
         classes=(str,),
@@ -241,7 +254,7 @@ def __init__(
 registry.register(
     _ColorStyle,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="color_mode",
         node_name="colorMode",
         classes=(ColorMode,),
@@ -513,7 +526,7 @@ def icon_href(self) -> Optional[str]:
 registry.register(
     IconStyle,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="scale",
         node_name="scale",
         classes=(float,),
@@ -525,7 +538,7 @@ def icon_href(self) -> Optional[str]:
 registry.register(
     IconStyle,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="heading",
         node_name="heading",
         classes=(float,),
@@ -537,7 +550,7 @@ def icon_href(self) -> Optional[str]:
 registry.register(
     IconStyle,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="icon",
         node_name="Icon",
         classes=(Icon,),
@@ -548,7 +561,7 @@ def icon_href(self) -> Optional[str]:
 registry.register(
     IconStyle,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="hot_spot",
         node_name="hotSpot",
         classes=(HotSpot,),
@@ -646,7 +659,7 @@ def __bool__(self) -> bool:
 registry.register(
     LineStyle,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="width",
         node_name="width",
         classes=(float,),
@@ -755,7 +768,7 @@ def __bool__(self) -> bool:
 registry.register(
     PolyStyle,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="fill",
         node_name="fill",
         classes=(bool,),
@@ -767,7 +780,7 @@ def __bool__(self) -> bool:
 registry.register(
     PolyStyle,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="outline",
         node_name="outline",
         classes=(bool,),
@@ -869,7 +882,7 @@ def __bool__(self) -> bool:
 registry.register(
     LabelStyle,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="scale",
         node_name="scale",
         classes=(float,),
@@ -1021,7 +1034,7 @@ def __bool__(self) -> bool:
 registry.register(
     BalloonStyle,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="bg_color",
         node_name="bgColor",
         classes=(str,),
@@ -1033,7 +1046,7 @@ def __bool__(self) -> bool:
 registry.register(
     BalloonStyle,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="text_color",
         node_name="textColor",
         classes=(str,),
@@ -1045,7 +1058,7 @@ def __bool__(self) -> bool:
 registry.register(
     BalloonStyle,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="text",
         node_name="text",
         classes=(str,),
@@ -1056,7 +1069,7 @@ def __bool__(self) -> bool:
 registry.register(
     BalloonStyle,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="display_mode",
         node_name="displayMode",
         classes=(DisplayMode,),
@@ -1148,7 +1161,7 @@ def __bool__(self) -> bool:
 registry.register(
     Style,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="styles",
         node_name="Style",
         classes=(
@@ -1258,7 +1271,7 @@ def __bool__(self) -> bool:
 registry.register(
     Pair,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="key",
         node_name="key",
         classes=(PairKey,),
@@ -1269,7 +1282,7 @@ def __bool__(self) -> bool:
 registry.register(
     Pair,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="style",
         node_name="Style",
         classes=(
@@ -1391,7 +1404,7 @@ def highlight(self) -> Optional[Union[StyleUrl, Style]]:
 registry.register(
     StyleMap,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="pairs",
         node_name="Pair",
         classes=(Pair,),
@@ -1399,15 +1412,3 @@ def highlight(self) -> Optional[Union[StyleUrl, Style]]:
         set_element=xml_subelement_list,
     ),
 )
-
-
-__all__ = [
-    "BalloonStyle",
-    "IconStyle",
-    "LabelStyle",
-    "LineStyle",
-    "PolyStyle",
-    "Style",
-    "StyleMap",
-    "StyleUrl",
-]
diff --git a/fastkml/times.py b/fastkml/times.py
index a2f35b5f..8f7a451e 100644
--- a/fastkml/times.py
+++ b/fastkml/times.py
@@ -41,7 +41,11 @@
 from fastkml.registry import RegistryItem
 from fastkml.registry import registry
 
-__all__ = ["KmlDateTime", "TimeSpan", "TimeStamp", "adjust_date_to_resolution"]
+__all__ = [
+    "KmlDateTime",
+    "TimeSpan",
+    "TimeStamp",
+]
 
 # regular expression to parse a gYearMonth string
 # year and month may be separated by an optional dash
diff --git a/fastkml/views.py b/fastkml/views.py
index 16ac27fe..57b30481 100644
--- a/fastkml/views.py
+++ b/fastkml/views.py
@@ -153,7 +153,7 @@ def __init__(
 registry.register(
     _AbstractView,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="longitude",
         node_name="longitude",
         classes=(float,),
@@ -165,7 +165,7 @@ def __init__(
 registry.register(
     _AbstractView,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="latitude",
         node_name="latitude",
         classes=(float,),
@@ -177,7 +177,7 @@ def __init__(
 registry.register(
     _AbstractView,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="altitude",
         node_name="altitude",
         classes=(float,),
@@ -189,7 +189,7 @@ def __init__(
 registry.register(
     _AbstractView,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="heading",
         node_name="heading",
         classes=(float,),
@@ -201,7 +201,7 @@ def __init__(
 registry.register(
     _AbstractView,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="tilt",
         node_name="tilt",
         classes=(float,),
@@ -317,7 +317,7 @@ def __repr__(self) -> str:
 registry.register(
     Camera,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="roll",
         node_name="roll",
         classes=(float,),
@@ -432,7 +432,7 @@ def __repr__(self) -> str:
 registry.register(
     LookAt,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="range",
         node_name="range",
         classes=(float,),
@@ -554,7 +554,7 @@ def __bool__(self) -> bool:
 registry.register(
     LatLonAltBox,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="north",
         node_name="north",
         classes=(float,),
@@ -565,7 +565,7 @@ def __bool__(self) -> bool:
 registry.register(
     LatLonAltBox,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="south",
         node_name="south",
         classes=(float,),
@@ -576,7 +576,7 @@ def __bool__(self) -> bool:
 registry.register(
     LatLonAltBox,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="east",
         node_name="east",
         classes=(float,),
@@ -587,7 +587,7 @@ def __bool__(self) -> bool:
 registry.register(
     LatLonAltBox,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="west",
         node_name="west",
         classes=(float,),
@@ -598,7 +598,7 @@ def __bool__(self) -> bool:
 registry.register(
     LatLonAltBox,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="min_altitude",
         node_name="minAltitude",
         classes=(float,),
@@ -610,7 +610,7 @@ def __bool__(self) -> bool:
 registry.register(
     LatLonAltBox,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="max_altitude",
         node_name="maxAltitude",
         classes=(float,),
@@ -716,7 +716,7 @@ def __bool__(self) -> bool:
 registry.register(
     Lod,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="min_lod_pixels",
         node_name="minLodPixels",
         classes=(float,),
@@ -728,7 +728,7 @@ def __bool__(self) -> bool:
 registry.register(
     Lod,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="max_lod_pixels",
         node_name="maxLodPixels",
         classes=(float,),
@@ -740,7 +740,7 @@ def __bool__(self) -> bool:
 registry.register(
     Lod,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="min_fade_extent",
         node_name="minFadeExtent",
         classes=(float,),
@@ -752,7 +752,7 @@ def __bool__(self) -> bool:
 registry.register(
     Lod,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="max_fade_extent",
         node_name="maxFadeExtent",
         classes=(float,),
@@ -852,7 +852,7 @@ def __bool__(self) -> bool:
 registry.register(
     Region,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="lat_lon_alt_box",
         node_name="LatLonAltBox",
         classes=(LatLonAltBox,),
@@ -863,7 +863,7 @@ def __bool__(self) -> bool:
 registry.register(
     Region,
     RegistryItem(
-        ns_ids=("kml",),
+        ns_ids=("kml", ""),
         attr_name="lod",
         node_name="Lod",
         classes=(Lod,),
diff --git a/pyproject.toml b/pyproject.toml
index c525ae04..50f8d47d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -22,7 +22,11 @@ classifiers = [
     "Programming Language :: Python :: 3.12",
     "Programming Language :: Python :: 3.13",
     "Programming Language :: Python :: 3.14",
+    "Programming Language :: Python :: Implementation :: CPython",
+    "Programming Language :: Python :: Implementation :: PyPy",
     "Topic :: Scientific/Engineering :: GIS",
+    "Topic :: Software Development :: Libraries",
+    "Topic :: Software Development :: Libraries :: Python Modules",
     "Topic :: Text Processing :: Markup :: XML",
     "Typing :: Typed",
 ]
@@ -191,8 +195,6 @@ target-version = "py38"
 [tool.ruff.lint]
 ignore = [
     "A002",
-    "ANN101",
-    "ANN102",
     "ANN401",
     "D203",
     "D212",
diff --git a/tests/hypothesis/feature_test.py b/tests/hypothesis/feature_test.py
index 6f289c45..f04bc8e8 100644
--- a/tests/hypothesis/feature_test.py
+++ b/tests/hypothesis/feature_test.py
@@ -29,6 +29,7 @@
 import fastkml.features
 import fastkml.gx
 import fastkml.links
+import fastkml.model
 import fastkml.styles
 import fastkml.views
 from tests.base import Lxml
@@ -419,6 +420,44 @@ def test_fuzz_placemark_styles(
         assert_str_roundtrip_terse(placemark)
         assert_str_roundtrip_verbose(placemark)
 
+    @given(
+        model=st.builds(
+            fastkml.model.Model,
+            altitude_mode=st.sampled_from(fastkml.enums.AltitudeMode),
+            location=st.builds(
+                fastkml.model.Location,
+                latitude=st.floats(
+                    allow_nan=False,
+                    allow_infinity=False,
+                    min_value=-90,
+                    max_value=90,
+                ).filter(lambda x: x != 0),
+                longitude=st.floats(
+                    allow_nan=False,
+                    allow_infinity=False,
+                    min_value=-180,
+                    max_value=180,
+                ).filter(lambda x: x != 0),
+                altitude=st.floats(allow_nan=False, allow_infinity=False).filter(
+                    lambda x: x != 0,
+                ),
+            ),
+            link=st.builds(fastkml.Link, href=urls()),
+        ),
+    )
+    def test_fuzz_placemark_model(
+        self,
+        model: fastkml.model.Model,
+    ) -> None:
+        placemark = fastkml.Placemark(
+            kml_geometry=model,
+        )
+
+        assert_repr_roundtrip(placemark)
+        assert_str_roundtrip(placemark)
+        assert_str_roundtrip_terse(placemark)
+        assert_str_roundtrip_verbose(placemark)
+
     @given(
         refresh_visibility=st.one_of(st.none(), st.booleans()),
         fly_to_view=st.one_of(st.none(), st.booleans()),
diff --git a/tests/hypothesis/model_test.py b/tests/hypothesis/model_test.py
new file mode 100644
index 00000000..ee318107
--- /dev/null
+++ b/tests/hypothesis/model_test.py
@@ -0,0 +1,288 @@
+# Copyright (C) 2024  Christian Ledermann
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+"""Hypothesis tests for the fastkml.model module."""
+
+import typing
+
+from hypothesis import given
+from hypothesis import strategies as st
+from hypothesis.provisional import urls
+
+import fastkml
+import fastkml.enums
+import fastkml.links
+import fastkml.model
+from tests.base import Lxml
+from tests.hypothesis.common import assert_repr_roundtrip
+from tests.hypothesis.common import assert_str_roundtrip
+from tests.hypothesis.common import assert_str_roundtrip_terse
+from tests.hypothesis.common import assert_str_roundtrip_verbose
+from tests.hypothesis.strategies import nc_name
+
+
+class TestLxml(Lxml):
+    @given(
+        altitude=st.one_of(
+            st.none(),
+            st.just(0.0),
+            st.floats(allow_nan=False, allow_infinity=False).filter(lambda x: x != 0),
+        ),
+        latitude=st.one_of(
+            st.none(),
+            st.just(0.0),
+            st.floats(
+                allow_nan=False,
+                allow_infinity=False,
+                min_value=-90,
+                max_value=90,
+            ),
+        ),
+        longitude=st.one_of(
+            st.none(),
+            st.just(0.0),
+            st.floats(
+                allow_nan=False,
+                allow_infinity=False,
+                min_value=-180,
+                max_value=180,
+            ),
+        ),
+    )
+    def test_fuzz_location(
+        self,
+        altitude: typing.Optional[float],
+        latitude: typing.Optional[float],
+        longitude: typing.Optional[float],
+    ) -> None:
+        location = fastkml.model.Location(
+            altitude=altitude,
+            latitude=latitude,
+            longitude=longitude,
+        )
+
+        assert_repr_roundtrip(location)
+        assert_str_roundtrip(location)
+        assert_str_roundtrip_terse(location)
+        assert_str_roundtrip_verbose(location)
+
+    @given(
+        heading=st.one_of(
+            st.none(),
+            st.just(0.0),
+            st.floats(
+                allow_nan=False,
+                allow_infinity=False,
+                min_value=-360,
+                max_value=360,
+            ).filter(lambda x: x != 0),
+        ),
+        tilt=st.one_of(
+            st.none(),
+            st.just(0.0),
+            st.floats(
+                allow_nan=False,
+                allow_infinity=False,
+                min_value=0,
+                max_value=180,
+            ).filter(lambda x: x != 0),
+        ),
+        roll=st.one_of(
+            st.none(),
+            st.just(0.0),
+            st.floats(
+                allow_nan=False,
+                allow_infinity=False,
+                min_value=-180,
+                max_value=180,
+            ).filter(lambda x: x != 0),
+        ),
+    )
+    def test_fuzz_orientation(
+        self,
+        heading: typing.Optional[float],
+        tilt: typing.Optional[float],
+        roll: typing.Optional[float],
+    ) -> None:
+        orientation = fastkml.model.Orientation(heading=heading, tilt=tilt, roll=roll)
+
+        assert_repr_roundtrip(orientation)
+        assert_str_roundtrip(orientation)
+        assert_str_roundtrip_terse(orientation)
+        assert_str_roundtrip_verbose(orientation)
+
+    @given(
+        x=st.one_of(st.none(), st.floats(allow_nan=False, allow_infinity=False)),
+        y=st.one_of(st.none(), st.floats(allow_nan=False, allow_infinity=False)),
+        z=st.one_of(st.none(), st.floats(allow_nan=False, allow_infinity=False)),
+    )
+    def test_fuzz_scale(
+        self,
+        x: typing.Optional[float],
+        y: typing.Optional[float],
+        z: typing.Optional[float],
+    ) -> None:
+        scale = fastkml.model.Scale(x=x, y=y, z=z)
+
+        assert_repr_roundtrip(scale)
+        assert_str_roundtrip(scale)
+        assert_str_roundtrip_terse(scale)
+        assert_str_roundtrip_verbose(scale)
+
+    @given(
+        target_href=st.one_of(st.none(), urls()),
+        source_href=st.one_of(st.none(), urls()),
+    )
+    def test_fuzz_alias(
+        self,
+        target_href: typing.Optional[str],
+        source_href: typing.Optional[str],
+    ) -> None:
+        alias = fastkml.model.Alias(target_href=target_href, source_href=source_href)
+
+        assert_repr_roundtrip(alias)
+        assert_str_roundtrip(alias)
+        assert_str_roundtrip_terse(alias)
+        assert_str_roundtrip_verbose(alias)
+
+    @given(
+        aliases=st.one_of(
+            st.none(),
+            st.lists(
+                st.builds(
+                    fastkml.model.Alias,
+                    source_href=urls(),
+                    target_href=urls(),
+                ),
+            ),
+        ),
+    )
+    def test_fuzz_resource_map(
+        self,
+        aliases: typing.Optional[typing.Iterable[fastkml.model.Alias]],
+    ) -> None:
+        resource_map = fastkml.model.ResourceMap(aliases=aliases)
+
+        assert_repr_roundtrip(resource_map)
+        assert_str_roundtrip(resource_map)
+        assert_str_roundtrip_terse(resource_map)
+        assert_str_roundtrip_verbose(resource_map)
+
+    @given(
+        id=st.one_of(st.none(), nc_name()),
+        target_id=st.one_of(st.none(), nc_name()),
+        altitude_mode=st.one_of(st.none(), st.sampled_from(fastkml.enums.AltitudeMode)),
+        location=st.one_of(
+            st.none(),
+            st.builds(
+                fastkml.model.Location,
+                altitude=st.floats(allow_nan=False, allow_infinity=False).filter(
+                    lambda x: x != 0,
+                ),
+                latitude=st.floats(
+                    allow_nan=False,
+                    allow_infinity=False,
+                    min_value=-90,
+                    max_value=90,
+                ),
+                longitude=st.floats(
+                    allow_nan=False,
+                    allow_infinity=False,
+                    min_value=-180,
+                    max_value=180,
+                ),
+            ),
+        ),
+        orientation=st.one_of(
+            st.none(),
+            st.builds(
+                fastkml.model.Orientation,
+                heading=st.floats(
+                    allow_nan=False,
+                    allow_infinity=False,
+                    min_value=-360,
+                    max_value=360,
+                ).filter(lambda x: x != 0),
+                tilt=st.floats(
+                    allow_nan=False,
+                    allow_infinity=False,
+                    min_value=0,
+                    max_value=180,
+                ).filter(lambda x: x != 0),
+                roll=st.floats(
+                    allow_nan=False,
+                    allow_infinity=False,
+                    min_value=-180,
+                    max_value=180,
+                ).filter(lambda x: x != 0),
+            ),
+        ),
+        scale=st.one_of(
+            st.none(),
+            st.builds(
+                fastkml.model.Scale,
+                x=st.floats(allow_nan=False, allow_infinity=False).filter(
+                    lambda x: x != 0,
+                ),
+                y=st.floats(allow_nan=False, allow_infinity=False).filter(
+                    lambda x: x != 0,
+                ),
+                z=st.floats(allow_nan=False, allow_infinity=False).filter(
+                    lambda x: x != 0,
+                ),
+            ),
+        ),
+        link=st.one_of(st.none(), st.builds(fastkml.Link, href=urls())),
+        resource_map=st.one_of(
+            st.none(),
+            st.builds(
+                fastkml.model.ResourceMap,
+                aliases=st.lists(
+                    st.builds(
+                        fastkml.model.Alias,
+                        source_href=urls(),
+                        target_href=urls(),
+                    ),
+                    min_size=1,
+                ),
+            ),
+        ),
+    )
+    def test_fuzz_model(
+        self,
+        id: typing.Optional[str],
+        target_id: typing.Optional[str],
+        altitude_mode: typing.Optional[fastkml.enums.AltitudeMode],
+        location: typing.Optional[fastkml.model.Location],
+        orientation: typing.Optional[fastkml.model.Orientation],
+        scale: typing.Optional[fastkml.model.Scale],
+        link: typing.Optional[fastkml.Link],
+        resource_map: typing.Optional[fastkml.model.ResourceMap],
+    ) -> None:
+        model = fastkml.model.Model(
+            id=id,
+            target_id=target_id,
+            altitude_mode=altitude_mode,
+            location=location,
+            orientation=orientation,
+            scale=scale,
+            link=link,
+            resource_map=resource_map,
+        )
+
+        assert_repr_roundtrip(model)
+        assert_str_roundtrip(model)
+        assert_str_roundtrip_terse(model)
+        assert_str_roundtrip_verbose(model)
diff --git a/tests/hypothesis/network_link_control_test.py b/tests/hypothesis/network_link_control_test.py
new file mode 100644
index 00000000..3ff1d963
--- /dev/null
+++ b/tests/hypothesis/network_link_control_test.py
@@ -0,0 +1,122 @@
+# Copyright (C) 2024  Christian Ledermann
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+"""Hypothesis tests for the fastkml.network_link_control module."""
+
+import typing
+
+from hypothesis import given
+from hypothesis import strategies as st
+
+import fastkml
+import fastkml.enums
+import fastkml.model
+import fastkml.views
+from tests.base import Lxml
+from tests.hypothesis.common import assert_repr_roundtrip
+from tests.hypothesis.common import assert_str_roundtrip
+from tests.hypothesis.common import assert_str_roundtrip_terse
+from tests.hypothesis.common import assert_str_roundtrip_verbose
+from tests.hypothesis.strategies import kml_datetimes
+from tests.hypothesis.strategies import xml_text
+
+
+class TestLxml(Lxml):
+    @given(
+        min_refresh_period=st.one_of(
+            st.none(),
+            st.floats(allow_nan=False, allow_infinity=False).filter(lambda x: x != 0),
+        ),
+        max_session_length=st.one_of(
+            st.none(),
+            st.floats(allow_nan=False, allow_infinity=False).filter(lambda x: x != -1),
+        ),
+        cookie=st.one_of(st.none(), xml_text()),
+        message=st.one_of(st.none(), xml_text()),
+        link_name=st.one_of(st.none(), xml_text()),
+        link_description=st.one_of(st.none(), xml_text()),
+        link_snippet=st.one_of(st.none(), xml_text()),
+        expires=st.one_of(
+            st.none(),
+            kml_datetimes(),
+        ),
+        view=st.one_of(
+            st.none(),
+            st.builds(
+                fastkml.views.Camera,
+                longitude=st.floats(
+                    allow_nan=False,
+                    allow_infinity=False,
+                    min_value=-180,
+                    max_value=180,
+                ).filter(lambda x: x != 0),
+                latitude=st.floats(
+                    allow_nan=False,
+                    allow_infinity=False,
+                    min_value=-90,
+                    max_value=90,
+                ).filter(lambda x: x != 0),
+                altitude=st.floats(allow_nan=False, allow_infinity=False).filter(
+                    lambda x: x != 0,
+                ),
+            ),
+            st.builds(
+                fastkml.views.LookAt,
+                longitude=st.floats(
+                    allow_nan=False,
+                    allow_infinity=False,
+                    min_value=-180,
+                    max_value=180,
+                ).filter(lambda x: x != 0),
+                latitude=st.floats(
+                    allow_nan=False,
+                    allow_infinity=False,
+                    min_value=-90,
+                    max_value=90,
+                ).filter(lambda x: x != 0),
+                altitude=st.floats(allow_nan=False, allow_infinity=False).filter(
+                    lambda x: x != 0,
+                ),
+            ),
+        ),
+    )
+    def test_fuzz_network_link_control(
+        self,
+        min_refresh_period: typing.Optional[float],
+        max_session_length: typing.Optional[float],
+        cookie: typing.Optional[str],
+        message: typing.Optional[str],
+        link_name: typing.Optional[str],
+        link_description: typing.Optional[str],
+        link_snippet: typing.Optional[str],
+        expires: typing.Optional[fastkml.KmlDateTime],
+        view: typing.Union[fastkml.Camera, fastkml.LookAt, None],
+    ) -> None:
+        nlc = fastkml.NetworkLinkControl(
+            min_refresh_period=min_refresh_period,
+            max_session_length=max_session_length,
+            cookie=cookie,
+            message=message,
+            link_name=link_name,
+            link_description=link_description,
+            link_snippet=link_snippet,
+            expires=expires,
+            view=view,
+        )
+
+        assert_repr_roundtrip(nlc)
+        assert_str_roundtrip(nlc)
+        assert_str_roundtrip_terse(nlc)
+        assert_str_roundtrip_verbose(nlc)
diff --git a/tests/hypothesis/overlay_test.py b/tests/hypothesis/overlay_test.py
index 5d7762ef..807e50bb 100644
--- a/tests/hypothesis/overlay_test.py
+++ b/tests/hypothesis/overlay_test.py
@@ -17,6 +17,7 @@
 
 import typing
 
+import pytest
 from hypothesis import given
 from hypothesis import strategies as st
 from pygeoif.hypothesis.strategies import epsg4326
@@ -31,6 +32,7 @@
 from tests.hypothesis.common import assert_str_roundtrip
 from tests.hypothesis.common import assert_str_roundtrip_terse
 from tests.hypothesis.common import assert_str_roundtrip_verbose
+from tests.hypothesis.strategies import xy
 
 
 class TestLxml(Lxml):
@@ -242,3 +244,68 @@ def test_fuzz_ground_overlay(
         assert_str_roundtrip(ground_overlay)
         assert_str_roundtrip_terse(ground_overlay)
         assert_str_roundtrip_verbose(ground_overlay)
+
+    @pytest.mark.parametrize(
+        "cls",
+        [
+            fastkml.overlays.OverlayXY,
+            fastkml.overlays.RotationXY,
+            fastkml.overlays.ScreenXY,
+            fastkml.overlays.Size,
+        ],
+    )
+    @given(
+        x=st.one_of(st.none(), st.floats(allow_nan=False, allow_infinity=False)),
+        y=st.one_of(st.none(), st.floats(allow_nan=False, allow_infinity=False)),
+        x_units=st.one_of(st.none(), st.sampled_from(fastkml.enums.Units)),
+        y_units=st.one_of(st.none(), st.sampled_from(fastkml.enums.Units)),
+    )
+    def test_fuzz_xy(
+        self,
+        cls: typing.Union[
+            typing.Type[fastkml.overlays.OverlayXY],
+            typing.Type[fastkml.overlays.RotationXY],
+            typing.Type[fastkml.overlays.ScreenXY],
+            typing.Type[fastkml.overlays.Size],
+        ],
+        x: typing.Optional[float],
+        y: typing.Optional[float],
+        x_units: typing.Optional[fastkml.enums.Units],
+        y_units: typing.Optional[fastkml.enums.Units],
+    ) -> None:
+        xy = cls(x=x, y=y, x_units=x_units, y_units=y_units)
+
+        assert_repr_roundtrip(xy)
+        assert_str_roundtrip(xy)
+        assert_str_roundtrip_terse(xy)
+        assert_str_roundtrip_verbose(xy)
+
+    @given(
+        overlay_xy=xy(fastkml.overlays.OverlayXY),
+        screen_xy=xy(fastkml.overlays.ScreenXY),
+        rotation_xy=xy(fastkml.overlays.RotationXY),
+        size=xy(fastkml.overlays.Size),
+        rotation=st.floats(min_value=-180, max_value=180).filter(lambda x: x != 0),
+    )
+    def test_screen_overlay(
+        self,
+        overlay_xy: typing.Optional[fastkml.overlays.OverlayXY],
+        screen_xy: typing.Optional[fastkml.overlays.ScreenXY],
+        rotation_xy: typing.Optional[fastkml.overlays.RotationXY],
+        size: typing.Optional[fastkml.overlays.Size],
+        rotation: typing.Optional[float],
+    ) -> None:
+        screen_overlay = fastkml.overlays.ScreenOverlay(
+            id="screen_overlay1",
+            name="screen_overlay",
+            overlay_xy=overlay_xy,
+            screen_xy=screen_xy,
+            rotation_xy=rotation_xy,
+            size=size,
+            rotation=rotation,
+        )
+
+        assert_repr_roundtrip(screen_overlay)
+        assert_str_roundtrip(screen_overlay)
+        assert_str_roundtrip_terse(screen_overlay)
+        assert_str_roundtrip_verbose(screen_overlay)
diff --git a/tests/hypothesis/strategies.py b/tests/hypothesis/strategies.py
index 4390510f..e070c450 100644
--- a/tests/hypothesis/strategies.py
+++ b/tests/hypothesis/strategies.py
@@ -178,6 +178,14 @@
     when=kml_datetimes(),
 )
 
+xy = partial(
+    st.builds,
+    x=st.floats(allow_nan=False, allow_infinity=False),
+    y=st.floats(allow_nan=False, allow_infinity=False),
+    x_units=st.sampled_from(fastkml.enums.Units),
+    y_units=st.sampled_from(fastkml.enums.Units),
+)
+
 
 @st.composite
 def query_strings(draw: st.DrawFn) -> str:
diff --git a/tests/model_test.py b/tests/model_test.py
new file mode 100644
index 00000000..fb10c150
--- /dev/null
+++ b/tests/model_test.py
@@ -0,0 +1,148 @@
+# Copyright (C) 2021 - 2023  Christian Ledermann
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+"""Test the kml overlay classes."""
+
+from pygeoif.geometry import Point
+
+import fastkml.links
+import fastkml.model
+from fastkml.enums import AltitudeMode
+from tests.base import Lxml
+from tests.base import StdLibrary
+
+
+class TestModel(StdLibrary):
+    def test_from_string(self) -> None:
+        doc = (
+            '<Model xmlns="http://www.opengis.net/kml/2.2">'
+            "<altitudeMode>absolute</altitudeMode>"
+            "<Location>"
+            "<longitude>-123.115776547816</longitude>"
+            "<latitude>49.279804095564</latitude>"
+            "<altitude>21.614010375743</altitude>"
+            "</Location>"
+            "<Scale><x>1</x><y>1</y><z>1</z></Scale>"
+            "<Link><href>http://barcelona.galdos.local/files/PublicLibrary.dae</href></Link>"
+            '<ResourceMap id="map01">'
+            "<Alias>"
+            "<targetHref>http://barcelona.galdos.local/images/Concrete2.jpg</targetHref>"
+            "<sourceHref>../images/Concrete.jpg</sourceHref>"
+            "</Alias>"
+            "</ResourceMap>"
+            "</Model>"
+        )
+
+        model = fastkml.model.Model.from_string(doc)
+
+        assert model.altitude_mode == AltitudeMode.absolute
+        assert model.geometry == Point(
+            -123.115776547816,
+            49.279804095564,
+            21.614010375743,
+        )
+        assert model == fastkml.model.Model(
+            altitude_mode=AltitudeMode.absolute,
+            location=fastkml.model.Location(
+                altitude=21.614010375743,
+                latitude=49.279804095564,
+                longitude=-123.115776547816,
+            ),
+            orientation=None,
+            scale=fastkml.model.Scale(
+                x=1.0,
+                y=1.0,
+                z=1.0,
+            ),
+            link=fastkml.links.Link(
+                href="http://barcelona.galdos.local/files/PublicLibrary.dae",
+            ),
+            resource_map=fastkml.model.ResourceMap(
+                aliases=[
+                    fastkml.model.Alias(
+                        target_href="http://barcelona.galdos.local/images/Concrete2.jpg",
+                        source_href="../images/Concrete.jpg",
+                    ),
+                ],
+            ),
+        )
+
+    def test_from_string_no_location(self) -> None:
+        doc = (
+            '<Model xmlns="http://www.opengis.net/kml/2.2">'
+            "<altitudeMode>absolute</altitudeMode>"
+            "<Scale><x>1</x><y>1</y><z>1</z></Scale>"
+            "<Link><href>http://barcelona.galdos.local/files/PublicLibrary.dae</href></Link>"
+            '<ResourceMap id="map01">'
+            "<Alias>"
+            "<targetHref>http://barcelona.galdos.local/images/Concrete2.jpg</targetHref>"
+            "<sourceHref>../images/Concrete.jpg</sourceHref>"
+            "</Alias>"
+            "</ResourceMap>"
+            "</Model>"
+        )
+
+        model = fastkml.model.Model.from_string(doc)
+
+        assert model.altitude_mode == AltitudeMode.absolute
+        assert model.geometry is None
+        assert not model
+
+    def test_from_string_invalid_location(self) -> None:
+        doc = (
+            '<Model xmlns="http://www.opengis.net/kml/2.2">'
+            "<altitudeMode>absolute</altitudeMode>"
+            "<Location>"
+            "<longitude></longitude>"
+            "<latitude>49.279804095564</latitude>"
+            "<altitude>21.614010375743</altitude>"
+            "</Location>"
+            "<Scale><x>1</x><y>1</y><z>1</z></Scale>"
+            "<Link><href>http://barcelona.galdos.local/files/PublicLibrary.dae</href></Link>"
+            '<ResourceMap id="map01">'
+            "<Alias>"
+            "<targetHref>http://barcelona.galdos.local/images/Concrete2.jpg</targetHref>"
+            "<sourceHref>../images/Concrete.jpg</sourceHref>"
+            "</Alias>"
+            "</ResourceMap>"
+            "</Model>"
+        )
+
+        model = fastkml.model.Model.from_string(doc)
+
+        assert model.altitude_mode == AltitudeMode.absolute
+        assert model.geometry is None
+        assert not model
+
+    def test_location_from_string_invalid_location(self) -> None:
+        doc = (
+            '<Location xmlns="http://www.opengis.net/kml/2.2">'
+            "<longitude></longitude>"
+            "<latitude>49.279804095564</latitude>"
+            "<altitude>21.614010375743</altitude>"
+            "</Location>"
+        )
+
+        location = fastkml.model.Location.from_string(doc)
+
+        assert location.altitude == 21.614010375743
+        assert location.latitude == 49.279804095564
+        assert location.geometry is None
+        assert not location
+
+
+class TestModelLxml(TestModel, Lxml):
+    pass
diff --git a/tests/network_link_control_test.py b/tests/network_link_control_test.py
new file mode 100644
index 00000000..5cfa0ac4
--- /dev/null
+++ b/tests/network_link_control_test.py
@@ -0,0 +1,81 @@
+# Copyright (C) 2021 - 2022  Christian Ledermann
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+"""Test the Network Link Control classes."""
+
+import datetime
+
+from dateutil.tz import tzutc
+
+from fastkml import views
+from fastkml.network_link_control import NetworkLinkControl
+from fastkml.times import KmlDateTime
+from tests.base import StdLibrary
+
+
+class TestStdLibrary(StdLibrary):
+    """Test with the standard library."""
+
+    def test_network_link_control_obj(self) -> None:
+        dt = datetime.datetime.now(tz=tzutc())
+        kml_datetime = KmlDateTime(dt=dt)
+        view = views.Camera()
+
+        network_control_obj = NetworkLinkControl(
+            min_refresh_period=1.1,
+            max_session_length=100.1,
+            cookie="cookie",
+            message="message",
+            link_name="link_name",
+            link_description="link_description",
+            link_snippet="link_snippet",
+            expires=kml_datetime,
+            view=view,
+        )
+
+        assert network_control_obj.min_refresh_period == 1.1
+        assert network_control_obj.max_session_length == 100.1
+        assert network_control_obj.cookie == "cookie"
+        assert network_control_obj.message == "message"
+        assert network_control_obj.link_name == "link_name"
+        assert network_control_obj.link_description == "link_description"
+        assert network_control_obj.link_snippet == "link_snippet"
+        assert str(network_control_obj.expires) == str(kml_datetime)
+        assert str(network_control_obj.view) == str(view)
+
+    def test_network_link_control_kml(self) -> None:
+        doc = (
+            '<kml:NetworkLinkControl xmlns:kml="http://www.opengis.net/kml/2.2">'
+            "<kml:minRefreshPeriod>432000</kml:minRefreshPeriod>"
+            "<kml:maxSessionLength>-1</kml:maxSessionLength>"
+            "<kml:linkSnippet>A Snippet</kml:linkSnippet>"
+            "<kml:expires>2008-05-30</kml:expires>"
+            "</kml:NetworkLinkControl>"
+        )
+
+        nc = NetworkLinkControl.from_string(doc)
+
+        dt = datetime.date(2008, 5, 30)
+        kml_datetime = KmlDateTime(dt=dt)
+
+        nc_obj = NetworkLinkControl(
+            min_refresh_period=432000,
+            max_session_length=-1,
+            link_snippet="A Snippet",
+            expires=kml_datetime,
+        )
+
+        assert nc == nc_obj
diff --git a/tests/overlays_test.py b/tests/overlays_test.py
index 3a7a71cf..0209e6ed 100644
--- a/tests/overlays_test.py
+++ b/tests/overlays_test.py
@@ -14,7 +14,9 @@
 # along with this library; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 
-"""Test the kml classes."""
+"""Test the kml overlay classes."""
+
+import contextlib
 
 from pygeoif import geometry as geo
 
@@ -24,15 +26,74 @@
 from fastkml import overlays
 from fastkml import views
 from fastkml.enums import AltitudeMode
+from fastkml.enums import Units
 from tests.base import Lxml
 from tests.base import StdLibrary
 
 
-class TestGroundOverlay(StdLibrary):
-    pass
+class TestScreenOverlay(StdLibrary):
+    def test_screen_overlay_from_string(self) -> None:
+        """Create a ScreenOverlay object with all optional parameters."""
+        doc = (
+            '<ScreenOverlay xmlns="http://www.opengis.net/kml/2.2">'
+            "<name>Simple crosshairs</name>"
+            "<visibility>0</visibility>"
+            "<description>This screen overlay uses fractional positioning to put the "
+            "image in the exact center of the screen</description>"
+            "<Icon>"
+            "<href>http://developers.google.com/kml/images/crosshairs.png</href>"
+            "</Icon>"
+            '<overlayXY x="0.5" y="0.5" xunits="fraction" yunits="fraction"/>'
+            '<screenXY x="0.5" y="0.5" xunits="fraction" yunits="fraction"/>'
+            '<rotationXY x="0.5" y="0.5" xunits="fraction" yunits="fraction"/>'
+            '<size x="0" y="0" xunits="pixels" yunits="pixels"/>'
+            "<rotation>-45</rotation>"
+            "</ScreenOverlay>"
+        )
+
+        screen_overlay = overlays.ScreenOverlay.from_string(doc)
+
+        assert screen_overlay == overlays.ScreenOverlay(
+            name="Simple crosshairs",
+            visibility=False,
+            description=(
+                "This screen overlay uses fractional positioning to put the image "
+                "in the exact center of the screen"
+            ),
+            icon=links.Icon(
+                href="http://developers.google.com/kml/images/crosshairs.png",
+            ),
+            overlay_xy=overlays.OverlayXY(
+                x=0.5,
+                y=0.5,
+                x_units=Units.fraction,
+                y_units=Units.fraction,
+            ),
+            screen_xy=overlays.ScreenXY(
+                x=0.5,
+                y=0.5,
+                x_units=Units.fraction,
+                y_units=Units.fraction,
+            ),
+            rotation_xy=overlays.RotationXY(
+                x=0.5,
+                y=0.5,
+                x_units=Units.fraction,
+                y_units=Units.fraction,
+            ),
+            size=overlays.Size(
+                x=0.0,
+                y=0.0,
+                x_units=Units.pixels,
+                y_units=Units.pixels,
+            ),
+            rotation=-45,
+        )
+        with contextlib.suppress(TypeError):
+            screen_overlay.validate()
 
 
-class TestGroundOverlayString(StdLibrary):
+class TestGroundOverlay(StdLibrary):
     def test_default_to_string(self) -> None:
         g = overlays.GroundOverlay()
 
@@ -362,11 +423,11 @@ def test_camera_initialization(self) -> None:
         assert po.view.roll == 60
 
 
-class TestGroundOverlayLxml(Lxml, TestGroundOverlay):
+class TestScreenOverlayLxml(Lxml, TestScreenOverlay):
     """Test with lxml."""
 
 
-class TestGroundOverlayStringLxml(Lxml, TestGroundOverlay):
+class TestGroundOverlayLxml(Lxml, TestGroundOverlay):
     """Test with lxml."""