2011-02-15

Examples of GML fragment in ISO 19115/19139 gmd:verticalElement

Next one is vertical.

Meteorology is really indifferent about the concept of CRS, so there is
bunch of altitude data without reference system specification. Luckily there
is EPSG:5714 registered as "msl height" so we can express it in globally
understood framework.

<gmd:verticalElement>
<gmd:EX_VerticalExtent id="boundingVerticalExtent">
<gmd:minimumValue>
<gco:Real>7.7</gco:Real>
</gmd:minimumValue>
<gmd:maximumValue>
<gco:Real>7.7</gco:Real>
</gmd:maximumValue>
<gmd:verticalCRS>
<gml:VerticalCRS gml:id="crs.msl_height">
<!-- EPSG:5714 simply means "msl height" -->
<gml:identifier codeSpace="urn:ogc:def:crs:EPSG::">5714</gml:identifier>
<!-- instruction in ISO 19136 12.2.3.4 -->
<gml:scope>not known</gml:scope>
<gml:verticalCS/>
<gml:verticalDatum />
</gml:VerticalCRS>
</gmd:verticalCRS>
</gmd:EX_VerticalExtent>
</gmd:verticalElement>

What if we use pressure coordinate or height above surface? I don't know.
Maybe WMO has to do something. It's just a creating couple of URI and code,
so it should be easy if people understands the way.

Examples of GML fragment in ISO 19115/19139 gmd:temporalElement

Sometimes ISO 19139 uses GML fragments to describe complex structure. But
it is too simply documented for novice to understand how to write it. I'm a
novice too, but unfortunately had to develop a guideline.....

One of the most often-used GML in 19139 should be temporal reference in
extent information. The simplest one should be an instance (single point on
a time axis):

<gmd:EX_TemporalExtent id="time_instance">
<gmd:extent>
<gml:TimeInstant gml:id="instant">
<gml:timePosition>1992-01-01T00:00:00Z</gml:timePosition>
</gml:TimeInstant>
</gmd:extent>
</gmd:EX_TemporalExtent>
</gmd:temporalElement>

Second most useful should be a time range:

<gmd:temporalElement>
<gmd:EX_TemporalExtent id="boundingTemporalExtent">
<gmd:extent>
<gml:TimePeriod gml:id="validTimeRange">
<gml:beginPosition>1992-01-01T00:00:00Z</gml:beginPosition>
<gml:endPosition>2007-12-31T00:00:00Z</gml:endPosition>
</gml:TimePeriod>
</gmd:extent>
</gmd:EX_TemporalExtent>
</gmd:temporalElement>

Really easy, isn't it?

The time in timePosition/beginPosition/endPosition can be one of:

- xs:date
- xs:gYearMonth
- xs:gYear
- xs:time
- xs:dateTime
- xs:anyURI
- xs:decimal

Decimal is intended for numeric time, for example GPS time in seconds. URI
is intended for indicating commonly-shared era, which I don't know how to
use.

<gml:endPosition
frame="tag:toyoda.eizi@gmail.com:2011-02-12:mytime_in_seconds">23131231231.1231</gml:endPosition>

But I believe it is more explicit to use gml:timeInterval.

<gml:TimePeriod gml:id="validTimeRange3">
<gml:beginPosition>1992-01-01T00:00:00Z</gml:beginPosition>
<gml:end/>
<gml:timeInterval unit="second">32.75232143</gml:timeInterval>
</gml:TimePeriod>

Complex calendar duration in ISO format can be shown in gml:duration.

<gml:TimePeriod gml:id="validTimeRange4">
<gml:beginPosition>1992-01-01T00:00:00Z</gml:beginPosition>
<gml:end/>
<gml:duration>P2Y2M3D</gml:duration>

</gml:TimePeriod>

The 2nd example can be verbose. I don't know benefit of this form, except
each of "begin" and "end" has id attribute.

<gmd:temporalElement>
<gmd:EX_TemporalExtent id="boundingTemporalExtent">
<gmd:extent>
<gml:TimePeriod gml:id="validTimeRange">
<gml:begin>
<gml:TimeInstant gml:id="begdate">
<gml:timePosition>1992-01-01T00:00:00Z</gml:timePosition>
</gml:TimeInstant>
</gml:begin>
<gml:end>
<gml:TimeInstant gml:id="enddate">
<gml:timePosition>2007-12-31T00:00:00Z</gml:timePosition>
</gml:TimeInstant>
</gml:end>
</gml:TimePeriod>
</gmd:extent>
</gmd:EX_TemporalExtent>
</gmd:temporalElement>

something like strcmp(3) in XSLT 1.0

I had a need to compare alphanumeric string. Unfortunately XSLT 1.0 does
not have string comparison, nor even function to extract character code from
character. So following example works only for string with letters and
numbers. All other characters are treated equally, just after "z" and "_"
of the dictionary order.

<xsl:template name="strcmpAN">
<xsl:param name="a"/>
<xsl:param name="b"/>
<xsl:variable name="ia">
<xsl:call-template name="charCodeAN">
<xsl:with-param name="s" select="$a"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="ib">
<xsl:call-template name="charCodeAN">
<xsl:with-param name="s" select="$b"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$a = $b">
<xsl:value-of select="number(0)"/>
</xsl:when>
<xsl:when test="string-length($b) = 0">
<xsl:value-of select="number(1)"/>
</xsl:when>
<xsl:when test="string-length($a) = 0">
<xsl:value-of select="number(-1)"/>
</xsl:when>
<xsl:when test="$ia &gt; $ib">
<xsl:value-of select="number(1)"/>
</xsl:when>
<xsl:when test="$ia &lt; $ib">
<xsl:value-of select="number(-1)"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="strcmpAN">
<xsl:with-param name="a" select="substring($a, 2)"/>
<xsl:with-param name="b" select="substring($b, 2)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template name="charCodeAN">
<xsl:param name="s"/>
<xsl:param name="i" select="1"/>
<xsl:variable name="CHARTABLE"
select="'
0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz_'"/>
<xsl:choose>
<xsl:when test="$i &gt; string-length($CHARTABLE)">
<xsl:value-of select="string-length($CHARTABLE) + 1"/>
</xsl:when>
<xsl:when test="substring($CHARTABLE, $i, 1) = substring($s, 1, 1)">
<xsl:value-of select="$i"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="charCodeAN">
<xsl:with-param name="s" select="$s"/>
<xsl:with-param name="i" select="$i + 1"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

2011-01-26

Reverse-engineered RelaxNG schema for GAW XML Catalogue

GAW (Global Atmospheric Watch) has its legacy catalogue system. I called it
legacy, but I don't mean it is bad or inferior. Rather I'm interested in
the fact that there already exists an operating network. Why the WIS don't
make use of it to achieve efficient implementation of catalogue? So I've
got an XML sample data from WDCGG and analysed its structure.
grammar {
start =
element wdcgg {
element station {
element station_name { xsd:token { maxLength = "64" } },
element id {
xsd:Name { length = "9" pattern = "\s*\w\w\w\d\d\d\w\d\d\s*" }
},
element latitude {
xsd:float { maxInclusive = "90" minInclusive = "-90" }
},
element longitude {
xsd:float { maxInclusive = "180" minInclusive = "-180" }
},
element altitude {
xsd:float { maxInclusive = "5079" minInclusive = "0" }
},
element wmo_region { WMORegion },
element gaw_category { GawCategory },
element country { xsd:token },
element organization { xsd:token },
element description { xsd:string },
element parameter {
element parameter_name {
xsd:NMTOKEN { maxLength = "7" pattern = "\s*[0-9A-Za-z]+\s*" }
},
element status_of_report { xsd:string },
element start_of_data { DateOrEmpty },
element end_of_data { DateOrEmpty },
element last_update { DateOrEmpty },
element contributor {
element organization { xsd:token },
element country { xsd:token }
}+,
element contact_person {
element name { xsd:token },
element organization { xsd:token },
element phone { xsd:token },
element fax { xsd:token },
element email { xsd:token }
}*
}*
}*
}
DateOrEmpty =
empty
| xsd:date { pattern = "\s*[0-9]{4}-[0-9]{2}-[0-9]{2}\s*" }
WMORegion =
"REGION I (Africa)"
| "REGION II (Asia)"
| "REGION III (South America)"
| "REGION IV (North and Central America)"
| "REGION V (South-West Pacific)"
| "REGION VI (Europe)"
| "ANTARCTICA"
GawCategory =
empty
| "Regional"
| "Global"
| "Contributing"
| "Non-GAW (international)"
}

2010-10-30

Forward proxy of HTTPS by Apache HTTPD

I wanted to set up an forward proxy server for a web site that uses
both HTTPS and HTTP protocols.
Okay it is well documented in
<http://httpd.apache.org/docs/2.2/en/mod/mod_proxy.html>.

One thing I had to find by trial and error is that https connection is
implemented by CONNECT method of HTTP and is not represented directly
in <Proxy> directive.
Instead we have to match URI in form "proxy:host:443".

# for HTTP
<Proxy http://{ORIGIN-SERVER}/*>
Order deny,allow
Allow from {CLIENT-IP}
</Proxy>

# for HTTPS
<Proxy proxy:{ORIGIN-SERVER}:443>
Order deny,allow
Allow from {CLIENT-IP}
</Proxy>

2010-10-21

ctags for XML Schema

require 'uri'
require 'rubygems'
require 'xml'

class App

def wputs str
$stderr.puts str if $VERBOSE
end

def eputs str
$stderr.puts str
end

def assert_equal test, right
raise "#{test} != #{right}" unless test == right
end

def getopts
while /^-/ === @argv.first
case opt = @argv.shift
when /^-o(.*)/ then @outfnam = $1
end
end
end

def initialize argv
@argv = argv.dup
@cache = {}
@names = {}
@outfnam = 'tags'
getopts
end

def help
puts <
vi tags generator
usage: ruby #$0 file.xsd ...
EOF
end

XSD_NS = 'http://www.w3.org/2001/XMLSchema'

def get1 uri, disp = nil
if @cache[uri.to_s] then
wputs "skipping #{disp or uri}"
return
end
doc = XML::Document.file(uri.path)
@cache[uri.to_s] = true
children = []
assert_equal(doc.root.namespaces.namespace.href, XSD_NS)
nodes = doc.find('/xs:schema/xs:import|/xs:schema/xs:include', 'xs'=>XSD_NS)
nodes.each { |node|
children.push node['schemaLocation'].to_s
}
nodes = nil
nodes = doc.find('/xs:schema/xs:*/@name', 'xs'=>XSD_NS)
nodes.each { |node|
name = node.value
if @names[name]
eputs "duplicated #{name} in #{uri.path} and #{@names[name]}"
end
@names[name] = uri.path
}
nodes = nil
doc = nil
for child in children
get1(uri + child, child)
end
end

def run1 filename
uri = URI('file:///' + filename)
get1(uri)
end

def run
for filename in @argv
run1 filename
end
self
end

def output
File.open(@outfnam, 'w') { |fp|
now = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
fp.puts "!_TAG_FILE_SORTED\t1\tsort=case-sensitive date=#{now}"
for name in @names.keys.sort
query = '/\["\']' + name.gsub(/\W/, '.') + '\["\']/'
fp.puts [name, @names[name], query].join("\t")
end
}
eputs "saved to #{@outfnam}"
end

def close
output
self
end

end

App.new(ARGV).run.close
When editing XMLs in vi, it is really powerful.
====
require 'uri'
require 'rubygems'
require 'xml'

class App

def wputs str
$stderr.puts str if $VERBOSE
end

def eputs str
$stderr.puts str
end

def assert_equal test, right
raise "#{test} != #{right}" unless test == right
end

def getopts
while /^-/ === @argv.first
case opt = @argv.shift
when /^-o(.*)/ then @outfnam = $1
end
end
end

def initialize argv
@argv = argv.dup
@cache = {}
@names = {}
@outfnam = 'tags'
getopts
end

def help
puts <
vi tags generator
usage: ruby #$0 file.xsd ...
EOF
end

XSD_NS = 'http://www.w3.org/2001/XMLSchema'

def get1 uri, disp = nil
if @cache[uri.to_s] then
wputs "skipping #{disp or uri}"
return
end
doc = XML::Document.file(uri.path)
@cache[uri.to_s] = true
children = []
assert_equal(doc.root.namespaces.namespace.href, XSD_NS)
nodes = doc.find('/xs:schema/xs:import|/xs:schema/xs:include', 'xs'=>XSD_NS)
nodes.each { |node|
children.push node['schemaLocation'].to_s
}
nodes = nil
nodes = doc.find('/xs:schema/xs:*/@name', 'xs'=>XSD_NS)
nodes.each { |node|
name = node.value
if @names[name]
eputs "duplicated #{name} in #{uri.path} and #{@names[name]}"
end
@names[name] = uri.path
}
nodes = nil
doc = nil
for child in children
get1(uri + child, child)
end
end

def run1 filename
uri = URI('file:///' + filename)
get1(uri)
end

def run
for filename in @argv
run1 filename
end
self
end

def output
File.open(@outfnam, 'w') { |fp|
now = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
fp.puts "!_TAG_FILE_SORTED\t1\tsort=case-sensitive date=#{now}"
for name in @names.keys.sort
query = '/\["\']' + name.gsub(/\W/, '.') + '\["\']/'
fp.puts [name, @names[name], query].join("\t")
end
}
eputs "saved to #{@outfnam}"
end

def close
output
self
end

end

App.new(ARGV).run.close