"""$Id: kml.py 1057 2009-07-21 21:54:14Z sa3ruby $"""
__author__ = "Gregor J. Rothfuss and Mark Pilgrim "
__version__ = "$Revision: 1057 $"
__copyright__ = "Copyright (c) 2002 Sam Ruby and Mark Pilgrim"
from base import validatorBase
from validators import *
import re
# This code tries to mimic the structure of the canonical KML XSD as much as possible.
# The KML XSD is at http://code.google.com/apis/kml/schema/kml21.xsd
# FeatureType from the XSD schema
#
class FeatureType(validatorBase):
def do_name(self):
return text(),noduplicates()
def do_visibility(self):
return zeroone(),noduplicates()
def do_open(self):
return zeroone(),noduplicates()
def do_address(self):
return nonhtml(),noduplicates()
def do_phoneNumber(self):
return text(),noduplicates() # todo: implement full check from http://www.koders.com/perl/fid426DF448FE99166A1AD0162538E583A0FA956EEA.aspx
def do_Snippet(self):
return Snippet(), noduplicates()
def do_description(self):
return text(), noduplicates()
def do_LookAt(self):
return LookAt(),noduplicates()
# TimePrimitive
def do_TimeStamp(self):
return TimeStamp(),noduplicates()
def do_TimeSpan(self):
return TimeSpan(),noduplicates()
# /TimePrimitive
def do_styleUrl(self):
return text(), noduplicates()
# StyleSelector
def do_Style(self):
return Style()
def do_StyleMap(self):
return StyleMap()
# /StyleSelector
# 2.0 only
def do_View(self):
return View(),noduplicates()
# /2.0 only
def do_Region(self):
return Region(), noduplicates()
def do_Metadata(self):
return Metadata()
def do_atom_link(self):
from link import link
return link()
def do_atom_author(self):
from author import author
return author()
# OverlayType from the XSD schema
#
class OverlayType(validatorBase):
def do_color(self):
return color(),noduplicates()
def do_drawOrder(self):
return Integer(),noduplicates()
def do_Icon(self):
return Icon(), noduplicates()
# ColorStyleType from the XSD schema
#
class ColorStyleType(validatorBase):
def do_color(self):
return color(),noduplicates()
def do_colorMode(self):
return colorMode(),noduplicates()
#
# Container from the XSD schema
#
class Container(validatorBase):
def do_Document(self):
return Document()
def do_Folder(self):
return Folder()
#
# Feature from the XSD schema
#
class Feature(validatorBase):
def do_Placemark(self):
return Placemark()
#
# Geometry from the XSD schema
#
class Geometry(Feature):
# TODO these should all be noduplicates(), but because they can appear
# inside MultiGeometry, they are not.
def do_Model(self):
return Model()
def do_LineString(self):
return LineString()
def do_LinearRing(self):
return LinearRing()
def do_Point(self):
return Point()
def do_Polygon(self):
return Polygon()
def do_MultiGeometry(self):
return MultiGeometry()
#
# GeometryElements from the XSD schema
#
class GeometryElements(Geometry):
def do_extrude(self):
return zeroone(),noduplicates()
def do_tessellate(self):
return zeroone(),noduplicates()
def do_altitudeMode(self):
return altitudeMode(),noduplicates()
#
# LinkType from the XSD schema
#
class LinkType(validatorBase):
def do_href(self):
return text(),noduplicates()
def do_refreshMode(self):
return refreshMode(),noduplicates()
def do_viewRefreshMode(self):
return viewRefreshMode(),noduplicates()
def do_viewRefreshTime(self):
return Float(), noduplicates()
def do_viewBoundScale(self):
return Float(), noduplicates()
def do_refreshVisibility(self):
return refreshVisibility(),noduplicates()
def do_refreshInterval(self):
return Float(), noduplicates()
def do_viewFormat(self):
return text(),noduplicates()
def do_httpQuery(self):
return text(),noduplicates()
#
# LookAtType from the XSD schema
#
class LookAtType(Feature):
def do_longitude(self):
return longitude(),noduplicates()
def do_latitude(self):
return latitude(),noduplicates()
def do_altitude(self):
return FloatWithNegative(),noduplicates()
def do_range(self):
return Float(),noduplicates()
def do_tilt(self):
return latitude(),noduplicates()
def do_heading(self):
return angle360(),noduplicates()
def do_altitudeMode(self):
return altitudeMode(),noduplicates()
#
# KML element.
#
class kml(validatorBase, Container, Feature):
from logging import TYPE_KML20, TYPE_KML21, TYPE_KML22
def do_NetworkLink(self):
return NetworkLink()
def do_GroundOverlay(self):
return GroundOverlay()
def do_ScreenOverlay(self):
return ScreenOverlay()
def do_NetworkLinkControl(self):
return NetworkLinkControl()
def do_atom_link(self):
from link import link
return link()
def do_atom_author(self):
from author import author
return author()
class NetworkLinkControl(validatorBase):
def do_minRefreshPeriod(self):
return Float(),noduplicates()
def do_linkName(self):
return text(),noduplicates()
def do_linkDescription(self):
return text(),noduplicates()
def do_cookie(self):
return text(),noduplicates()
def do_message(self):
return text(), noduplicates()
def do_linkSnippet(self):
return Snippet(), noduplicates()
def do_expires(self):
return w3cdtf(),noduplicates()
def do_Update(self):
return Update(),noduplicates()
def do_LookAt(self):
return LookAt(),noduplicates()
def do_View(self):
return View(),noduplicates()
class Update(validatorBase):
def validate(self):
if not "targetHref" in self.children:
self.log(MissingElement({"parent":self.name, "element":"targetHref"}))
def do_targetHref(self):
return text(),noduplicates()
# todo: child validation
def do_Change(self):
return noduplicates()
# todo: child validation
def do_Update(self):
return noduplicates()
# todo: child validation
def do_Delete(self):
return noduplicates()
class NetworkLink(validatorBase, FeatureType, Feature):
def validate(self):
if not "Link" in self.children and not "Url" in self.children:
self.log(MissingElement({"parent":self.name, "element":"Link"}))
def do_targetHref(self):
return Update(),noduplicates()
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_refreshInterval(self):
return Float(), noduplicates()
def do_flyToView(self):
return zeroone(),noduplicates()
def do_Link(self):
return Link(),noduplicates()
def do_Url(self):
return Url(),noduplicates()
class Document(validatorBase, FeatureType, Container, Feature):
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_ScreenOverlay(self):
return ScreenOverlay()
def do_GroundOverlay(self):
return GroundOverlay()
def do_NetworkLink(self):
return NetworkLink()
def do_Schema(self):
return Schema(), noduplicates()
class Schema(validatorBase):
def getExpectedAttrNames(self):
return [(None, u'name'), (None, u'parent')]
def do_SimpleField(self):
return SchemaField()
def do_SimpleArrayField(self):
return SchemaField()
def do_ObjField(self):
return SchemaField()
def do_ObjArrayField(self):
return SchemaField()
class SchemaField(validatorBase):
def getExpectedAttrNames(self):
return [
(None, u'name'),
(None, u'type'),
]
def validate(self):
self.validate_required_attribute((None,'name'), text)
self.validate_required_attribute((None,'type'), SchemaFieldType)
class Placemark(validatorBase, FeatureType, Geometry):
def prevalidate(self):
if not self.attrs.has_key((None,"id")):
self.log(MissingId({"parent":self.name, "element":"id"}))
self.validate_optional_attribute((None,'id'), unique('id',self.parent))
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_GeometryCollection(self):
return GeometryCollection()
class MultiGeometry(Geometry):
# TODO: check for either geometry or multigeometry in feature, but not both?
def getExpectedAttrNames(self):
return [(None, u'id')]
class ScreenOverlay(validatorBase, FeatureType, OverlayType):
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_geomColor(self):
return geomColor(),noduplicates()
def do_overlayXY(self):
return overlayxy(), noduplicates()
def do_screenXY(self):
return overlayxy(), noduplicates()
def do_rotationXY(self):
return overlayxy(), noduplicates()
def do_size(self):
return overlayxy(), noduplicates()
class GroundOverlay(validatorBase, FeatureType, OverlayType):
def validate(self):
if not "LatLonBox" in self.children:
self.log(MissingElement({"parent":self.name, "element":"LatLonBox"}))
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_altitude(self):
return FloatWithNegative(),noduplicates()
def do_altitudeMode(self):
return altitudeMode(),noduplicates()
def do_geomColor(self):
return geomColor(),noduplicates()
def do_LatLonBox(self):
return LatLonBox(), noduplicates()
class overlayxy(validatorBase):
def getExpectedAttrNames(self):
return [
(None, u'x'),
(None, u'y'),
(None, u'xunits'),
(None, u'yunits'),
]
def validate(self):
self.validate_required_attribute((None,'x'), FloatWithNegative)
self.validate_required_attribute((None,'y'), FloatWithNegative)
self.validate_required_attribute((None,'xunits'), kmlunits)
self.validate_required_attribute((None,'yunits'), kmlunits)
class Region(validatorBase):
def validate(self):
if not "LatLonAltBox" in self.children:
self.log(MissingElement({"parent":self.name, "element":"LatLonAltBox"}))
def do_LatLonAltBox(self):
return LatLonAltBox(), noduplicates()
def do_Lod(self):
return Lod(), noduplicates()
class LatLonBox(validatorBase):
def getExpectedAttrNames(self):
return [(None, u'id')]
def validate(self):
if not "north" in self.children:
self.log(MissingElement({"parent":self.name, "element":"north"}))
if not "south" in self.children:
self.log(MissingElement({"parent":self.name, "element":"south"}))
if not "east" in self.children:
self.log(MissingElement({"parent":self.name, "element":"east"}))
if not "west" in self.children:
self.log(MissingElement({"parent":self.name, "element":"west"}))
def do_north(self):
return latitude(),noduplicates()
def do_south(self):
return latitude(),noduplicates()
def do_east(self):
return longitude(),noduplicates()
def do_west(self):
return longitude(),noduplicates()
def do_rotation(self):
return longitude(),noduplicates()
class LatLonAltBox(validatorBase, LatLonBox):
def do_minAltitude(self):
return Float(),noduplicates()
def do_maxAltitude(self):
return Float(), noduplicates()
def do_altitudeMode(self):
return altitudeMode(),noduplicates()
class Lod(validatorBase):
def do_minLodPixels(self):
return Float(),noduplicates()
def do_maxLodPixels(self):
return Float(),noduplicates()
def do_minFadeExtent(self):
return Float(),noduplicates()
def do_maxFadeExtent(self):
return Float(),noduplicates()
class Metadata(validatorBase):
# TODO do smarter validation here
def validate(self):
return noduplicates()
class Snippet(text):
def validate(self):
return nonhtml(),noduplicates()
def getExpectedAttrNames(self):
return [(None, u'maxLines')]
class Folder(validatorBase, FeatureType, Container, Feature):
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_NetworkLink(self):
return NetworkLink()
def do_GroundOverlay(self):
return GroundOverlay()
def do_ScreenOverlay(self):
return ScreenOverlay()
class LookAt(validatorBase, LookAtType):
def getExpectedAttrNames(self):
return [(None, u'id')]
class StyleMap(validatorBase):
def validate(self):
if not "Pair" in self.children:
self.log(MissingElement({"parent":self.name, "element":"Pair"}))
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_Pair(self):
return Pair()
class Style(validatorBase):
def prevalidate(self):
self.validate_optional_attribute((None,'id'), unique('id',self.parent))
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_LineStyle(self):
return LineStyle(), noduplicates()
def do_PolyStyle(self):
return PolyStyle(), noduplicates()
def do_IconStyle(self):
return IconStyle(), noduplicates()
def do_ListStyle(self):
return ListStyle(), noduplicates()
def do_LabelStyle(self):
return LabelStyle(), noduplicates()
def do_BalloonStyle(self):
return BalloonStyle(), noduplicates()
def do_scale(self):
return Float(),noduplicates()
def do_labelColor(self):
return labelColor(),noduplicates()
class IconStyle(validatorBase, ColorStyleType):
def prevalidate(self):
self.validate_optional_attribute((None,'id'), unique('id',self.parent))
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_heading(self):
return angle360(),noduplicates()
def do_Icon(self):
return Icon(),noduplicates()
def do_scale(self):
return Float(),noduplicates()
def do_hotSpot(self):
return overlayxy(), noduplicates()
class Icon(validatorBase):
def validate(self):
if not 'href' in self.children:
self.log(MissingElement({"parent":self.name, "element":"href"}))
def do_href(self):
# if not self.getFeedType() == TYPE_KML20 and self.startswith('root://'):
# self.log(DeprecatedRootHref())
return text(),noduplicates() # would be url, but has these weird root://
def do_x(self):
return noiconoffset()
def do_y(self):
return noiconoffset()
def do_w(self):
return noiconoffset()
def do_h(self):
return noiconoffset()
def do_refreshInterval(self):
return Float(), noduplicates()
def do_refreshMode(self):
return refreshMode(), noduplicates()
def do_viewRefreshMode(self):
return viewRefreshMode(), noduplicates()
def do_viewRefreshTime(self):
return Float(), noduplicates()
def do_viewBoundScale(self):
return Float(), noduplicates()
class BalloonStyle(validatorBase):
def prevalidate(self):
self.validate_optional_attribute((None,'id'), unique('id',self.parent))
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_textColor(self):
return color(),noduplicates()
def do_bgColor(self):
return color(),noduplicates()
def do_color(self):
return color(),noduplicates()
def do_text(self):
return text(),noduplicates()
class ListStyle(validatorBase):
def prevalidate(self):
self.validate_optional_attribute((None,'id'), unique('id',self.parent))
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_bgColor(self):
return color(),noduplicates()
def do_ItemIcon(self):
return ItemIcon()
def do_listItemType(self):
return listItemType(),noduplicates()
def do_scale(self):
return Float(),noduplicates()
class ItemIcon(validatorBase):
def validate(self):
if not 'href' in self.children:
self.log(MissingElement({"parent":self.name, "element":"href"}))
def do_href(self):
return text(),noduplicates()
def do_state(self):
return itemIconState(),noduplicates()
class LabelStyle(validatorBase, ColorStyleType):
def prevalidate(self):
self.validate_optional_attribute((None,'id'), unique('id',self.parent))
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_labelColor(self):
return labelColor(),noduplicates()
def do_scale(self):
return Float(),noduplicates()
class LineStyle(validatorBase, ColorStyleType):
def prevalidate(self):
self.validate_optional_attribute((None,'id'), unique('id',self.parent))
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_width(self):
return Float(),noduplicates()
class PolyStyle(validatorBase, ColorStyleType):
def prevalidate(self):
self.validate_optional_attribute((None,'id'), unique('id',self.parent))
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_fill(self):
return zeroone(), noduplicates()
def do_outline(self):
return zeroone(), noduplicates()
class Link(validatorBase, LinkType):
def getExpectedAttrNames(self):
return [(None, u'id')]
class Pair(validatorBase):
def validate(self):
if not 'key' in self.children:
self.log(MissingElement({"parent":self.name, "element":"key"}))
if not 'styleUrl' in self.children:
self.log(MissingElement({"parent":self.name, "element":"styleUrl"}))
def do_key(self):
return styleState(),noduplicates()
def do_styleUrl(self):
return text(),noduplicates()
class Point(validatorBase, GeometryElements):
def validate(self):
if not "coordinates" in self.children:
self.log(MissingElement({"parent":self.name, "element":"coordinates"}))
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_coordinates(self):
return coordinates()
class Model(validatorBase):
# TODO seems to me that Location and Orientation ought to be required?
def validate(self):
if not "Link" in self.children:
self.log(MissingElement({"parent":self.name, "element":"Link"}))
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_altitudeMode(self):
return altitudeMode(), noduplicates()
def do_Location(self):
return Location(), noduplicates()
def do_Orientation(self):
return Orientation(), noduplicates()
def do_Scale(self):
return Scale(), noduplicates()
def do_Link(self):
return Link(), noduplicates()
def do_ResourceMap(self):
return ResourceMap(), noduplicates()
class ResourceMap(validatorBase):
def do_Alias(self):
return Alias()
class Alias(validatorBase):
def do_targetHref(self):
return text(),noduplicates()
def do_sourceHref(self):
return text(),noduplicates()
class Location(validatorBase):
# TODO they are loosely defined in the schema, but 0,0,0 makes no sense.
def validate(self):
if not "longitude" in self.children:
self.log(MissingElement({"parent":self.name, "element":"longitude"}))
if not "latitude" in self.children:
self.log(MissingElement({"parent":self.name, "element":"latitude"}))
if not "altitude" in self.children:
self.log(MissingElement({"parent":self.name, "element":"altitude"}))
def do_longitude(self):
return longitude(), noduplicates()
def do_latitude(self):
return latitude(), noduplicates()
def do_altitude(self):
return FloatWithNegative(), noduplicates()
class Scale(validatorBase):
def do_x(self):
return Float(), noduplicates()
def do_y(self):
return Float(), noduplicates()
def do_z(self):
return Float(), noduplicates()
class Orientation(validatorBase):
def do_heading(self):
return angle360(), noduplicates()
def do_tilt(self):
return angle360(), noduplicates()
def do_roll(self):
return angle360(), noduplicates()
class Polygon(validatorBase, GeometryElements):
def validate(self):
if not "outerBoundaryIs" in self.children:
self.log(MissingElement({"parent":self.name, "element":"outerBoundaryIs"}))
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_outerBoundaryIs(self):
return boundary(), noduplicates()
def do_innerBoundaryIs(self):
return boundary()
class boundary(validatorBase):
def validate(self):
if not "LinearRing" in self.children:
self.log(MissingElement({"parent":self.name, "element":"LinearRing"}))
def do_LinearRing(self):
return LinearRing()
class LineString(validatorBase, GeometryElements):
def validate(self):
if not "coordinates" in self.children:
self.log(MissingElement({"parent":self.name, "element":"coordinates"}))
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_coordinates(self):
return coordinates(), noduplicates()
class LinearRing(validatorBase, GeometryElements):
def validate(self):
if not "coordinates" in self.children:
self.log(MissingElement({"parent":self.name, "element":"coordinates"}))
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_coordinates(self):
return coordinates(), noduplicates()
class TimeSpan(validatorBase):
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_begin(self):
return w3cdtf(),noduplicates()
def do_end(self):
return w3cdtf(),noduplicates()
class TimeStamp(validatorBase):
def validate(self):
if not "when" in self.children:
self.log(MissingElement({"parent":self.name, "element":"when"}))
def getExpectedAttrNames(self):
return [(None, u'id')]
def do_when(self):
return unbounded_w3cdtf(),noduplicates()
class kmlunits(enumeration):
error = InvalidKmlUnits
valuelist = [
"fraction", "pixels", "insetPixels"
]
class colorMode(enumeration):
error = InvalidColorMode
valuelist = [
"normal", "random"
]
class refreshMode(enumeration):
error = InvalidRefreshMode
valuelist = [
"onChange", "onInterval", "onExpire"
]
class viewRefreshMode(enumeration):
error = InvalidViewRefreshMode
valuelist = [
"never", "onRequest", "onStop", "onRegion"
]
class styleState(enumeration):
error = InvalidStyleState
valuelist = [
"normal", "highlight"
]
class listItemType(enumeration):
error = InvalidListItemType
valuelist = [
"radioFolder", "check", "checkHideChildren", "checkOffOnly"
]
class itemIconState(enumeration):
error = InvalidItemIconState
valuelist = [
"open", "closed", "error", "fetching0", "fetching1", "fetching2",
"open error", "closed error", "fetching0 error", "fetching1 error",
"fetching2 error"
]
class altitudeMode(enumeration):
error = InvalidAltitudeMode
valuelist = [
"clampToGround", "relativeToGround", "absolute"
]
class SchemaFieldType(enumeration):
error = InvalidSchemaFieldType
valuelist = [
"string", "int", "uint", "short", "ushort", "float", "double","bool"
]
#
# Deprecated in 2.0
#
class antialias(validatorBase):
def prevalidate(self):
self.log(Deprecated({"element":self.name, "replacement":"none"}))
def validate(self):
return zeroone(),noduplicates()
class View(validatorBase, LookAtType):
def prevalidate(self):
self.log(Deprecated({"element":self.name, "replacement":"LookAt"}))
def getExpectedAttrNames(self):
return [(None, u'id')]
#
# Deprecated in 2.1
#
class labelColor(text):
def prevalidate(self):
if not self.getFeedType() == TYPE_KML20:
self.log(Deprecated({"element":self.name, "replacement":"LabelStyle"}))
def validate(self):
if not re.match("([a-f]|[A-F]|[0-9]){8}",self.value):
return self.log(InvalidColor({'value':self.value}))
class geomColor(text):
def prevalidate(self):
if not self.getFeedType() == TYPE_KML20:
self.log(Deprecated({"element":self.name, "replacement":"color"}))
def validate(self):
if not re.match("([a-f]|[A-F]|[0-9]){8}",self.value):
return self.log(InvalidColor({'value':self.value}))
class geomScale(text):
def prevalidate(self):
if not self.getFeedType() == TYPE_KML20:
self.log(Deprecated({"element":self.name, "replacement":"scale"}))
def validate(self):
return Float()
class GeometryCollection(validatorBase, Geometry):
def prevalidate(self):
if not self.getFeedType() == TYPE_KML20:
self.log(Deprecated({"element":self.name, "replacement":"MultiGeometry"}))
def getExpectedAttrNames(self):
return [(None, u'id')]
class Url(validatorBase, LinkType):
def prevalidate(self):
if not self.getFeedType() == TYPE_KML20:
self.log(Deprecated({"element":self.name, "replacement":"Link"}))
class refreshVisibility(validatorBase):
def prevalidate(self):
if not self.getFeedType() == TYPE_KML20:
self.log(Deprecated({"element":self.name, "replacement":"Update"}))
def validate(self):
return zeroone, noduplicates()
# In theory, the spec also supports things like .2 if unit is fractions. ugh.
class noiconoffset(text):
def validate(self):
if not self.getFeedType() == TYPE_KML20:
self.log(Deprecated({"element":self.name, "replacement":"Icon"}))
return Integer(), noduplicates()
#
# Validators
#
class zeroone(text):
def normalizeWhitespace(self):
pass
def validate(self):
if not self.value.lower() in ['0','1']:
self.log(InvalidZeroOne({"parent":self.parent.name, "element":self.name,"value":self.value}))
class color(text):
def validate(self):
if not re.match("^([a-f]|[A-F]|[0-9]){8}$",self.value):
return self.log(InvalidColor({'value':self.value}))
class coordinates(text):
def validate(self):
values = self.value.strip().split()
for value in values:
# ensure that commas are only used to separate lat and long (and alt)
if not re.match('^[-+.0-9]+,[-+.0-9]+(,[-+.0-9]+)?$',
value.strip()):
return self.log(InvalidKmlCoordList({'value':self.value}))
# Now validate individual coordinates
point = value.split(',');
# First coordinate is longitude
try:
lon = float(point[0].strip())
if lon > 180 or lon < -180:
raise ValueError
else:
self.log(ValidLongitude({"parent":self.parent.name, "element":self.name, "value":lon}))
except ValueError:
self.log(InvalidKmlLongitude({"parent":self.parent.name, "element":self.name, "value":lon}))
# Second coordinate is latitude
try:
lat = float(point[1].strip())
if lat > 90 or lat < -90:
raise ValueError
else:
self.log(ValidLatitude({"parent":self.parent.name, "element":self.name, "value":lat}))
except ValueError:
self.log(InvalidKmlLatitude({"parent":self.parent.name, "element":self.name, "value":lat}))
# Third coordinate value (altitude) has to be float, if present
if len(point) == 3:
if not re.match('\d+\.?\d*$', point[2]):
self.log(InvalidFloat({"attr":self.name, "value":point[2]}))
class angle360(text):
def validate(self):
try:
angle = float(self.value)
if angle > 360 or angle < -360:
raise ValueError
else:
self.log(ValidAngle({"parent":self.parent.name, "element":self.name, "value":self.value}))
except ValueError:
self.log(InvalidAngle({"parent":self.parent.name, "element":self.name, "value":self.value}))
class FloatWithNegative(text):
def validate(self, name=None):
if not re.match('-?\d+\.?\d*$', self.value):
self.log(InvalidFloat({"attr":name or self.name, "value":self.value}))