<commit_before>import logging
import os

from settings import (
	ADD_FOLDER_TO_FILE_NAME, CONVERSIONS, EXPORT_PATH, UPLOAD_PATH, XSL_PATH
)

logger = logging.getLogger(__name__)

def append_folder_to_file_name(file_path):
	"""
	Add the parent folder name between the file name and extension.

	So "/grandparent/parent/name.ext"
	Becomes "/grandparent/parent/name-parent.ext".

	This allows the file names to be unique if the parent folders are
	merged into one folder.
	"""

	# ["/grandparent/parent", "name.ext"]
	folder_path, file_name = os.path.split(file_path)

	# ["/grandparent", "parent"]
	grandparent_path, parent_name = os.path.split(folder_path)

	# ["name", ".ext"]
	file_base_name, file_ext = os.path.splitext(file_name)

	# ["name", "-", "parent", ".ext"]
	new_name = ''.join([file_base_name, '-', parent_name, file_ext])

	# ["/grandparent/parent", "name-parent.ext"]
	return os.path.join(folder_path, new_name)

def check_paths(*paths):
	"""
	Return True if all paths exist, False if any are invalid.
	"""

	for f in paths:
		if not os.path.exists(f):
			return False

	return True

def file_is_newer(newer_file, older_file):
	"""
	Return True if the first file is newer than the second.

	Make sure each argument is the full path to the file.
	"""

	return os.path.getmtime(newer_file) > os.path.getmtime(newer_file)
	#return os.path.getctime(newer_file) > os.path.getctime(newer_file)

def find_updated_files(force_conversion=False, conversions=None):
	"""
	Generate names of XML files that need to be converted.

	The "allowed" function still applies even if `force_conversion` is True.

	Parameters
	----------

	force_conversion : bool
		Perform conversion even if the converted file is newer.

	conversions : list
		List of conversions to perform.
		Each is a tuple containing the XSL name, output folder and a function to
		check if conversion is allowed.
	"""

	xsl_path = lambda xsl: os.path.join(XSL_PATH, xsl)
	out_path = lambda folder: os.path.join(EXPORT_PATH, folder)

	if conversions is None:
		conversions = CONVERSIONS

	# Only include conversions where the XSL and output folder exist.
	full_conversion_details = [
		(xsl_path(x), out_path(o), f)
		for x, o, f in conversions
		if check_paths(xsl_path(x), out_path(o))
	]

	# Loop the contents of the input folder.
	for file_name in os.listdir(UPLOAD_PATH):
		# Ignore non-xml files.
		if not file_name.endswith('.xml'):
			continue

		# Get the path to the current file.
		input_file = os.path.join(UPLOAD_PATH, file_name)

		# Loop each of the conversion types.
		for xsl_path, output_folder, allowed in full_conversion_details:
			# Check for a function to check if conversion is allowed.
			if allowed is not None:
				# If conversion not allowed, skip to next item.
				if not allowed(input_file):
					continue

			# File path that will be output by the conversion.
			output_file = os.path.join(output_folder, file_name)

			# Folder to store any failed conversions.
			err_file = os.path.join(output_folder + '-error', file_name)

			# Tuple with file paths used in the conversion.
			paths = (input_file, xsl_path, output_file, err_file)

			# Do conversion if it is forced.
			if force_conversion:
				yield paths

			# Do conversion if it has not been done before.
			elif not os.path.exists(output_file):
				yield paths

			# Do conversion if the input file is newer.
			elif file_is_newer(input_file, output_file):
				yield paths

def get_files_in_folder(folder, ext=''):
	logger.debug('Checking for %s in %s', ext, folder)

	# Loop the contents of the folder.
	for file_name in os.listdir(folder):
		# Skip files with the wrong extension.
		if not file_name.endswith(ext):
			continue

		yield os.path.join(folder, file_name)

def get_input_path(output_path):
	"""
	Get the original file name from a converted file name.
	"""

	# ["/grandparent/parent", "file.ext"]
	folder_path, file_name = os.path.split(output_path)

	# ["/grandparent", "parent"]
	grandparent_path, parent_name = os.path.split(folder_path)

	# Check if file name is actually `file-parent.ext`.
	if ADD_FOLDER_TO_FILE_NAME:
		# ["name-parent", ".ext"]
		file_base_name, file_ext = os.path.splitext(file_name)

		dash_parent = '-{}'.format(parent_name)

		# Make sure the file name includes the parent.
		if file_base_name.endswith(dash_parent):
			file_base_name = file_base_name[:-len(dash_parent)]

		file_name = '{}{}'.format(file_base_name, file_ext)

	return os.path.join(UPLOAD_PATH, file_name)
<commit_msg>Add doc string to get files in folders function<commit_after>import logging
import os

from settings import (
	ADD_FOLDER_TO_FILE_NAME, CONVERSIONS, EXPORT_PATH, UPLOAD_PATH, XSL_PATH
)

logger = logging.getLogger(__name__)

def append_folder_to_file_name(file_path):
	"""
	Add the parent folder name between the file name and extension.

	So "/grandparent/parent/name.ext"
	Becomes "/grandparent/parent/name-parent.ext".

	This allows the file names to be unique if the parent folders are
	merged into one folder.
	"""

	# ["/grandparent/parent", "name.ext"]
	folder_path, file_name = os.path.split(file_path)

	# ["/grandparent", "parent"]
	grandparent_path, parent_name = os.path.split(folder_path)

	# ["name", ".ext"]
	file_base_name, file_ext = os.path.splitext(file_name)

	# ["name", "-", "parent", ".ext"]
	new_name = ''.join([file_base_name, '-', parent_name, file_ext])

	# ["/grandparent/parent", "name-parent.ext"]
	return os.path.join(folder_path, new_name)

def check_paths(*paths):
	"""
	Return True if all paths exist, False if any are invalid.
	"""

	for f in paths:
		if not os.path.exists(f):
			return False

	return True

def file_is_newer(newer_file, older_file):
	"""
	Return True if the first file is newer than the second.

	Make sure each argument is the full path to the file.
	"""

	return os.path.getmtime(newer_file) > os.path.getmtime(newer_file)
	#return os.path.getctime(newer_file) > os.path.getctime(newer_file)

def find_updated_files(force_conversion=False, conversions=None):
	"""
	Generate names of XML files that need to be converted.

	The "allowed" function still applies even if `force_conversion` is True.

	Parameters
	----------

	force_conversion : bool
		Perform conversion even if the converted file is newer.

	conversions : list
		List of conversions to perform.
		Each is a tuple containing the XSL name, output folder and a function to
		check if conversion is allowed.
	"""

	xsl_path = lambda xsl: os.path.join(XSL_PATH, xsl)
	out_path = lambda folder: os.path.join(EXPORT_PATH, folder)

	if conversions is None:
		conversions = CONVERSIONS

	# Only include conversions where the XSL and output folder exist.
	full_conversion_details = [
		(xsl_path(x), out_path(o), f)
		for x, o, f in conversions
		if check_paths(xsl_path(x), out_path(o))
	]

	# Loop the contents of the input folder.
	for file_name in os.listdir(UPLOAD_PATH):
		# Ignore non-xml files.
		if not file_name.endswith('.xml'):
			continue

		# Get the path to the current file.
		input_file = os.path.join(UPLOAD_PATH, file_name)

		# Loop each of the conversion types.
		for xsl_path, output_folder, allowed in full_conversion_details:
			# Check for a function to check if conversion is allowed.
			if allowed is not None:
				# If conversion not allowed, skip to next item.
				if not allowed(input_file):
					continue

			# File path that will be output by the conversion.
			output_file = os.path.join(output_folder, file_name)

			# Folder to store any failed conversions.
			err_file = os.path.join(output_folder + '-error', file_name)

			# Tuple with file paths used in the conversion.
			paths = (input_file, xsl_path, output_file, err_file)

			# Do conversion if it is forced.
			if force_conversion:
				yield paths

			# Do conversion if it has not been done before.
			elif not os.path.exists(output_file):
				yield paths

			# Do conversion if the input file is newer.
			elif file_is_newer(input_file, output_file):
				yield paths

def get_files_in_folder(folder, ext=''):
	"""
	Generate names of files in a folder.

	Parameters
	----------

	folder : str
		Path to the folder to generate file names from.

	ext : str
		Optional extension filter.
	"""

	logger.debug('Checking for %s in %s', ext, folder)

	# Loop the contents of the folder.
	for file_name in os.listdir(folder):
		# Skip files with the wrong extension.
		if not file_name.endswith(ext):
			continue

		yield os.path.join(folder, file_name)

def get_input_path(output_path):
	"""
	Get the original file name from a converted file name.
	"""

	# ["/grandparent/parent", "file.ext"]
	folder_path, file_name = os.path.split(output_path)

	# ["/grandparent", "parent"]
	grandparent_path, parent_name = os.path.split(folder_path)

	# Check if file name is actually `file-parent.ext`.
	if ADD_FOLDER_TO_FILE_NAME:
		# ["name-parent", ".ext"]
		file_base_name, file_ext = os.path.splitext(file_name)

		dash_parent = '-{}'.format(parent_name)

		# Make sure the file name includes the parent.
		if file_base_name.endswith(dash_parent):
			file_base_name = file_base_name[:-len(dash_parent)]

		file_name = '{}{}'.format(file_base_name, file_ext)

	return os.path.join(UPLOAD_PATH, file_name)
<|endoftext|>


<commit_before>from collections import OrderedDict

class Point(object):
    """Contains information about for each scan point

    Attributes:
        positions (dict): Dict of str position_name -> float position for each
            scannable dimension. E.g. {"x": 0.1, "y": 2.2}
        lower (dict): Dict of str position_name -> float lower_bound for each
            scannable dimension. E.g. {"x": 0.95, "y": 2.15}
        upper (dict): Dict of str position_name -> float upper_bound for each
            scannable dimension. E.g. {"x": 1.05, "y": 2.25}
        indexes (list): List of int indexes for each dataset dimension, fastest
            changing last. E.g. [15]
    """

    def __init__(self):
        self.positions = OrderedDict()
        self.lower = OrderedDict()
        self.upper = OrderedDict()
        self.indexes = []
<commit_msg>Update Point to use normal dictionaries for its coordinates<commit_after>

class Point(object):
    """Contains information about for each scan point

    Attributes:
        positions (dict): Dict of str position_name -> float position for each
            scannable dimension. E.g. {"x": 0.1, "y": 2.2}
        lower (dict): Dict of str position_name -> float lower_bound for each
            scannable dimension. E.g. {"x": 0.95, "y": 2.15}
        upper (dict): Dict of str position_name -> float upper_bound for each
            scannable dimension. E.g. {"x": 1.05, "y": 2.25}
        indexes (list): List of int indexes for each dataset dimension, fastest
            changing last. E.g. [15]
    """

    def __init__(self):
        self.positions = {}
        self.lower = {}
        self.upper = {}
        self.indexes = []
<|endoftext|>


<commit_before># encoding: utf-8
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
#
# Author: Trung Do (chin.bimbo@gmail.com)
#
from __future__ import division
from __future__ import unicode_literals

def minimize_task(task):
    """
    task objects are a little large, scrub them of some of the
    nested arrays
    :param task: task cluster normalized object
    :return: altered object
    """

    task.action.timings = None
    task.action.etl = None
    task.build.build = None
    task.build.task = {"id": task.build.task.id}
    task.etl = None
    task.repo.changeset.files = None
    task.task.artifacts = None
    task.task.created = None
    task.task.command = None
    task.task.env = None
    task.task.expires = None
    task.task.retries = None
    task.task.routes = None
    task.task.run = None
    task.task.runs = None
    task.task.scopes = None
    task.task.tags = None

<commit_msg>more scrubbing of task<commit_after># encoding: utf-8
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
#
# Author: Trung Do (chin.bimbo@gmail.com)
#
from __future__ import division
from __future__ import unicode_literals

def minimize_task(task):
    """
    task objects are a little large, scrub them of some of the
    nested arrays
    :param task: task cluster normalized object
    :return: altered object
    """

    task.action.timings = None
    task.action.etl = None
    task.build.build = None
    task.build.task = {"id": task.build.task.id}
    task.etl = None
    task.repo.changeset.files = None
    task.task.artifacts = None
    task.task.created = None
    task.task.command = None
    task.task.env = None
    task.task.expires = None
    task.task.retries = None
    task.task.routes = None
    task.task.run = None
    task.task.runs = None
    task.task.scopes = None
    task.task.tags = None
    task.task.sigining = None
    task.task.features= None
    task.task.image = None

<|endoftext|>


<commit_before># Copyright 2015 ClusterHQ. See LICENSE file for details.

from .constants import (
    MAXNAMELEN,
)

from ._libzfs_core import (
    lzc_create,
    lzc_clone,
    lzc_rollback,
    lzc_snapshot,
    lzc_snap,
    lzc_destroy_snaps,
    lzc_bookmark,
    lzc_get_bookmarks,
    lzc_destroy_bookmarks,
    lzc_snaprange_space,
    lzc_hold,
    lzc_release,
    lzc_get_holds,
    lzc_send,
    lzc_send_space,
    lzc_receive,
    lzc_recv,
    lzc_exists,
)

__all__ = [
    'ctypes',
    'exceptions',
    'MAXNAMELEN',
    'lzc_create',
    'lzc_clone',
    'lzc_rollback',
    'lzc_snapshot',
    'lzc_snap',
    'lzc_destroy_snaps',
    'lzc_bookmark',
    'lzc_get_bookmarks',
    'lzc_destroy_bookmarks',
    'lzc_snaprange_space',
    'lzc_hold',
    'lzc_release',
    'lzc_get_holds',
    'lzc_send',
    'lzc_send_space',
    'lzc_receive',
    'lzc_recv',
    'lzc_exists',
]

# vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4

<commit_msg>add docstring libzfs package<commit_after># Copyright 2015 ClusterHQ. See LICENSE file for details.
'''
Python wrappers for **libzfs_core** library.

*libzfs_core* is intended to be a stable, committed interface for programmatic
administration of ZFS.
This wrapper provides one-to-one wrappers for libzfs_core API functions,
but the signatures and types are more natural to Python.
nvlists are wrapped as dictionaries or lists depending on their usage.
Some parameters have default values depending on typical use for
increased convenience.
Output parameters are not used and return values are directly returned.
Enumerations and bit flags become strings and lists of strings in Python.
Errors are reported as exceptions rather than integer errno-style
error codes.  The wrapper takes care to provide one-to-many mapping
of the error codes to the exceptions by interpreting a context
in which the error code is produced.
'''

from .constants import (
    MAXNAMELEN,
)

from ._libzfs_core import (
    lzc_create,
    lzc_clone,
    lzc_rollback,
    lzc_snapshot,
    lzc_snap,
    lzc_destroy_snaps,
    lzc_bookmark,
    lzc_get_bookmarks,
    lzc_destroy_bookmarks,
    lzc_snaprange_space,
    lzc_hold,
    lzc_release,
    lzc_get_holds,
    lzc_send,
    lzc_send_space,
    lzc_receive,
    lzc_recv,
    lzc_exists,
)

__all__ = [
    'ctypes',
    'exceptions',
    'MAXNAMELEN',
    'lzc_create',
    'lzc_clone',
    'lzc_rollback',
    'lzc_snapshot',
    'lzc_snap',
    'lzc_destroy_snaps',
    'lzc_bookmark',
    'lzc_get_bookmarks',
    'lzc_destroy_bookmarks',
    'lzc_snaprange_space',
    'lzc_hold',
    'lzc_release',
    'lzc_get_holds',
    'lzc_send',
    'lzc_send_space',
    'lzc_receive',
    'lzc_recv',
    'lzc_exists',
]

# vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4

<|endoftext|>


<commit_before>#!/usr/bin/env python

"""
    A getscp application.
"""

import argparse
import logging
import os
import socket
import sys
import time

from pydicom import read_file
from pydicom.dataset import Dataset
from pydicom.uid import ExplicitVRLittleEndian, ImplicitVRLittleEndian, \
    ExplicitVRBigEndian

from pynetdicom3 import AE, QueryRetrieveSOPClassList, StorageSOPClassList

logger = logging.Logger('getscp')
stream_logger = logging.StreamHandler()
formatter = logging.Formatter('%(levelname).1s: %(message)s')
stream_logger.setFormatter(formatter)
logger.addHandler(stream_logger)
logger.setLevel(logging.ERROR)

def _setup_argparser():
    """Setup the command line arguments"""
    # Description
    parser = argparse.ArgumentParser(
        description="The getscp application implements a Service Class "
                    "Provider (SCP) for the Query/Retrieve (QR) Service Class "
                    "and the Basic Worklist Management (BWM) Service Class. "
                    "getscp only supports query functionality using the C-GET "
                    "message. It receives query keys from an SCU and sends a "
                    "response. The application can be used to test SCUs of the "
                    "QR and BWM Service Classes.",
        usage="getscp [options] port")

    # Parameters
    req_opts = parser.add_argument_group('Parameters')
    req_opts.add_argument("port",
                          help="TCP/IP port number to listen on",
                          type=int)

    # General Options
    gen_opts = parser.add_argument_group('General Options')
    gen_opts.add_argument("--version",
                          help="print version information and exit",
                          action="store_true")
    gen_opts.add_argument("--arguments",
                          help="print expanded command line arguments",
                          action="store_true")
    gen_opts.add_argument("-q", "--quiet",
                          help="quiet mode, print no warnings and errors",
                          action="store_true")
    gen_opts.add_argument("-v", "--verbose",
                          help="verbose mode, print processing details",
                          action="store_true")
    gen_opts.add_argument("-d", "--debug",
                          help="debug mode, print debug information",
                          action="store_true")
    gen_opts.add_argument("-ll", "--log-level", metavar='[l]',
                          help="use level l for the logger (fatal, error, warn, "
                               "info, debug, trace)",
                          type=str,
                          choices=['fatal', 'error', 'warn',
                                   'info', 'debug', 'trace'])
    gen_opts.add_argument("-lc", "--log-config", metavar='[f]',
                          help="use config file f for the logger",
                          type=str)

    # Network Options
    net_opts = parser.add_argument_group('Network Options')
    net_opts.add_argument("-aet", "--aetitle", metavar='[a]etitle',
                          help="set my AE title (default: GETSCP)",
                          type=str,
                          default='GETSCP')
    net_opts.add_argument("-to", "--timeout", metavar='[s]econds',
                          help="timeout for connection requests",
                          type=int,
                          default=None)
    net_opts.add_argument("-ta", "--acse-timeout", metavar='[s]econds',
                          help="timeout for ACSE messages",
                          type=int,
                          default=60)
    net_opts.add_argument("-td", "--dimse-timeout", metavar='[s]econds',
                          help="timeout for DIMSE messages",
                          type=int,
                          default=None)
    net_opts.add_argument("-pdu", "--max-pdu", metavar='[n]umber of bytes',
                          help="set max receive pdu to n bytes",
                          type=int,
                          default=16384)

    # Transfer Syntaxes
    ts_opts = parser.add_argument_group('Preferred Transfer Syntaxes')
    ts_opts.add_argument("-x=", "--prefer-uncompr",
                         help="prefer explicit VR local byte order (default)",
                         action="store_true")
    ts_opts.add_argument("-xe", "--prefer-little",
                         help="prefer explicit VR little endian TS",
                         action="store_true")
    ts_opts.add_argument("-xb", "--prefer-big",
                         help="prefer explicit VR big endian TS",
                         action="store_true")
    ts_opts.add_argument("-xi", "--implicit",
                         help="accept implicit VR little endian TS only",
                         action="store_true")

    return parser.parse_args()

args = _setup_argparser()

if args.verbose:
    logger.setLevel(logging.INFO)

if args.debug:
    logger.setLevel(logging.DEBUG)
    pynetdicom_logger = logging.getLogger('pynetdicom3')
    pynetdicom_logger.setLevel(logging.DEBUG)

logger.debug('$getscp.py v{0!s} {1!s} $'.format('0.1.0', '2016-04-11'))
logger.debug('')

# Validate port
if isinstance(args.port, int):
    test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    test_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    try:
        test_socket.bind((os.popen('hostname').read()[:-1], args.port))
    except socket.error:
        logger.error("Cannot listen on port {0:d}, insufficient priveleges".format(args.port))
        sys.exit()

# Set Transfer Syntax options
transfer_syntax = [ImplicitVRLittleEndian,
                   ExplicitVRLittleEndian,
                   ExplicitVRBigEndian]

if args.implicit:
    transfer_syntax = [ImplicitVRLittleEndian]

if args.prefer_little and ExplicitVRLittleEndian in transfer_syntax:
        transfer_syntax.remove(ExplicitVRLittleEndian)
        transfer_syntax.insert(0, ExplicitVRLittleEndian)

if args.prefer_big and ExplicitVRBigEndian in transfer_syntax:
        transfer_syntax.remove(ExplicitVRBigEndian)
        transfer_syntax.insert(0, ExplicitVRBigEndian)

def on_c_get(dataset):
    """Implement the on_c_get callback"""
    basedir = '../../../test/dicom_files/'
    dcm_files = ['RTImageStorage.dcm']
    dcm_files = [os.path.join(basedir, x) for x in dcm_files]
    yield len(dcm_files)

    for dcm in dcm_files:
        ds = read_file(dcm, force=True)
        yield 0xFF00, ds

scp_classes = [x for x in StorageSOPClassList]
scp_classes.extend(QueryRetrieveSOPClassList)

# Create application entity
ae = AE(ae_title=args.aetitle,
        port=args.port,
        scu_sop_class=[],
        scp_sop_class=scp_classes,
        transfer_syntax=transfer_syntax)

ae.maximum_pdu_size = args.max_pdu

# Set timeouts
ae.network_timeout = args.timeout
ae.acse_timeout = args.acse_timeout
ae.dimse_timeout = args.dimse_timeout

ae.on_c_get = on_c_get

ae.start()
<commit_msg>Update getscp path to test file<commit_after>#!/usr/bin/env python

"""
    A getscp application.
"""

import argparse
import logging
import os
import socket
import sys
import time

from pydicom import read_file
from pydicom.dataset import Dataset
from pydicom.uid import ExplicitVRLittleEndian, ImplicitVRLittleEndian, \
    ExplicitVRBigEndian

from pynetdicom3 import AE, QueryRetrieveSOPClassList, StorageSOPClassList

logger = logging.Logger('getscp')
stream_logger = logging.StreamHandler()
formatter = logging.Formatter('%(levelname).1s: %(message)s')
stream_logger.setFormatter(formatter)
logger.addHandler(stream_logger)
logger.setLevel(logging.ERROR)

def _setup_argparser():
    """Setup the command line arguments"""
    # Description
    parser = argparse.ArgumentParser(
        description="The getscp application implements a Service Class "
                    "Provider (SCP) for the Query/Retrieve (QR) Service Class "
                    "and the Basic Worklist Management (BWM) Service Class. "
                    "getscp only supports query functionality using the C-GET "
                    "message. It receives query keys from an SCU and sends a "
                    "response. The application can be used to test SCUs of the "
                    "QR and BWM Service Classes.",
        usage="getscp [options] port")

    # Parameters
    req_opts = parser.add_argument_group('Parameters')
    req_opts.add_argument("port",
                          help="TCP/IP port number to listen on",
                          type=int)

    # General Options
    gen_opts = parser.add_argument_group('General Options')
    gen_opts.add_argument("--version",
                          help="print version information and exit",
                          action="store_true")
    gen_opts.add_argument("--arguments",
                          help="print expanded command line arguments",
                          action="store_true")
    gen_opts.add_argument("-q", "--quiet",
                          help="quiet mode, print no warnings and errors",
                          action="store_true")
    gen_opts.add_argument("-v", "--verbose",
                          help="verbose mode, print processing details",
                          action="store_true")
    gen_opts.add_argument("-d", "--debug",
                          help="debug mode, print debug information",
                          action="store_true")
    gen_opts.add_argument("-ll", "--log-level", metavar='[l]',
                          help="use level l for the logger (fatal, error, warn, "
                               "info, debug, trace)",
                          type=str,
                          choices=['fatal', 'error', 'warn',
                                   'info', 'debug', 'trace'])
    gen_opts.add_argument("-lc", "--log-config", metavar='[f]',
                          help="use config file f for the logger",
                          type=str)

    # Network Options
    net_opts = parser.add_argument_group('Network Options')
    net_opts.add_argument("-aet", "--aetitle", metavar='[a]etitle',
                          help="set my AE title (default: GETSCP)",
                          type=str,
                          default='GETSCP')
    net_opts.add_argument("-to", "--timeout", metavar='[s]econds',
                          help="timeout for connection requests",
                          type=int,
                          default=None)
    net_opts.add_argument("-ta", "--acse-timeout", metavar='[s]econds',
                          help="timeout for ACSE messages",
                          type=int,
                          default=60)
    net_opts.add_argument("-td", "--dimse-timeout", metavar='[s]econds',
                          help="timeout for DIMSE messages",
                          type=int,
                          default=None)
    net_opts.add_argument("-pdu", "--max-pdu", metavar='[n]umber of bytes',
                          help="set max receive pdu to n bytes",
                          type=int,
                          default=16384)

    # Transfer Syntaxes
    ts_opts = parser.add_argument_group('Preferred Transfer Syntaxes')
    ts_opts.add_argument("-x=", "--prefer-uncompr",
                         help="prefer explicit VR local byte order (default)",
                         action="store_true")
    ts_opts.add_argument("-xe", "--prefer-little",
                         help="prefer explicit VR little endian TS",
                         action="store_true")
    ts_opts.add_argument("-xb", "--prefer-big",
                         help="prefer explicit VR big endian TS",
                         action="store_true")
    ts_opts.add_argument("-xi", "--implicit",
                         help="accept implicit VR little endian TS only",
                         action="store_true")

    return parser.parse_args()

args = _setup_argparser()

if args.verbose:
    logger.setLevel(logging.INFO)

if args.debug:
    logger.setLevel(logging.DEBUG)
    pynetdicom_logger = logging.getLogger('pynetdicom3')
    pynetdicom_logger.setLevel(logging.DEBUG)

logger.debug('$getscp.py v{0!s} {1!s} $'.format('0.1.0', '2016-04-11'))
logger.debug('')

# Validate port
if isinstance(args.port, int):
    test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    test_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    try:
        test_socket.bind((os.popen('hostname').read()[:-1], args.port))
    except socket.error:
        logger.error("Cannot listen on port {0:d}, insufficient priveleges".format(args.port))
        sys.exit()

# Set Transfer Syntax options
transfer_syntax = [ImplicitVRLittleEndian,
                   ExplicitVRLittleEndian,
                   ExplicitVRBigEndian]

if args.implicit:
    transfer_syntax = [ImplicitVRLittleEndian]

if args.prefer_little and ExplicitVRLittleEndian in transfer_syntax:
        transfer_syntax.remove(ExplicitVRLittleEndian)
        transfer_syntax.insert(0, ExplicitVRLittleEndian)

if args.prefer_big and ExplicitVRBigEndian in transfer_syntax:
        transfer_syntax.remove(ExplicitVRBigEndian)
        transfer_syntax.insert(0, ExplicitVRBigEndian)

def on_c_get(dataset):
    """Implement the on_c_get callback"""
    basedir = '../../tests/dicom_files/'
    dcm_files = ['RTImageStorage.dcm']
    dcm_files = [os.path.join(basedir, x) for x in dcm_files]
    yield len(dcm_files)

    for dcm in dcm_files:
        ds = read_file(dcm, force=True)
        yield 0xFF00, ds

scp_classes = [x for x in StorageSOPClassList]
scp_classes.extend(QueryRetrieveSOPClassList)

# Create application entity
ae = AE(ae_title=args.aetitle,
        port=args.port,
        scu_sop_class=[],
        scp_sop_class=scp_classes,
        transfer_syntax=transfer_syntax)

ae.maximum_pdu_size = args.max_pdu

# Set timeouts
ae.network_timeout = args.timeout
ae.acse_timeout = args.acse_timeout
ae.dimse_timeout = args.dimse_timeout

ae.on_c_get = on_c_get

ae.start()
<|endoftext|>


<commit_before>"""Holds various Version strings of MPF.

This modules holds the MPF version strings, including the version of BCP it
needs and the config file version it needs.

It's used internally for all sorts of things, from printing the output of the
`mpf --version` command, to making sure any processes connected via BCP are
the proper versions, to automatically triggering new builds and deployments to
PyPI.

"""

__version__ = '0.56.0-dev.1'
'''The full version of MPF.'''

__short_version__ = '0.56'
'''The major.minor version of MPF.'''

__bcp_version__ = '1.1'
'''The version of BCP this build of MPF uses.'''

__config_version__ = '5'
'''The config file version this build of MPF uses.'''

__show_version__ = '5'
'''The show format version this build of MPF uses.'''

# pylint: disable-msg=invalid-name
version = "MPF v{}".format(__version__)
'''A friendly version string for this build of MPF.'''

# pylint: disable-msg=invalid-name
extended_version = "MPF v{}, Config version:{}, Show version: {}, " \
                   "BCP version:{}".format(__version__, __config_version__,
                                           __show_version__, __bcp_version__)
'''An extended version string that includes the MPF version, show version,
and BCP versions used in this build of MPF.'''

if "dev" in __version__:
    # pylint: disable-msg=invalid-name
    log_url = "https://docs.missionpinball.org/en/dev/logs/{}.html"
else:
    # pylint: disable-msg=invalid-name
    log_url = "https://docs.missionpinball.org/en/{}/logs/{{}}.html".format(__short_version__)

__api__ = ['version',
           '__short_version__',
           '__bcp_version__',
           '__config_version__',
           '__show_version__',
           'version',
           'extended_version',
           'log_url']
<commit_msg>0.56.0-dev.2<commit_after>"""Holds various Version strings of MPF.

This modules holds the MPF version strings, including the version of BCP it
needs and the config file version it needs.

It's used internally for all sorts of things, from printing the output of the
`mpf --version` command, to making sure any processes connected via BCP are
the proper versions, to automatically triggering new builds and deployments to
PyPI.

"""

__version__ = '0.56.0-dev.2'
'''The full version of MPF.'''

__short_version__ = '0.56'
'''The major.minor version of MPF.'''

__bcp_version__ = '1.1'
'''The version of BCP this build of MPF uses.'''

__config_version__ = '5'
'''The config file version this build of MPF uses.'''

__show_version__ = '5'
'''The show format version this build of MPF uses.'''

# pylint: disable-msg=invalid-name
version = "MPF v{}".format(__version__)
'''A friendly version string for this build of MPF.'''

# pylint: disable-msg=invalid-name
extended_version = "MPF v{}, Config version:{}, Show version: {}, " \
                   "BCP version:{}".format(__version__, __config_version__,
                                           __show_version__, __bcp_version__)
'''An extended version string that includes the MPF version, show version,
and BCP versions used in this build of MPF.'''

if "dev" in __version__:
    # pylint: disable-msg=invalid-name
    log_url = "https://docs.missionpinball.org/en/dev/logs/{}.html"
else:
    # pylint: disable-msg=invalid-name
    log_url = "https://docs.missionpinball.org/en/{}/logs/{{}}.html".format(__short_version__)

__api__ = ['version',
           '__short_version__',
           '__bcp_version__',
           '__config_version__',
           '__show_version__',
           'version',
           'extended_version',
           'log_url']
<|endoftext|>


<commit_before># Copyright (c) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# DreamHost Qauntum Extensions
# Copyright 2012 New Dream Network, LLC (DreamHost)
# @author: Murali Raju, New Dream Network, LLC (DreamHost)
# @author: Mark Mcclain, New Dream Network, LLC (DreamHost)

from datetime import datetime
import logging
import netaddr
import re

import sqlalchemy as sa
from sqlalchemy import Column, String
from sqlalchemy import orm
from sqlalchemy.orm import validates

from quantum.api import api_common as common
from quantum.common import utils
from quantum.db import model_base
from quantum.openstack.common import timeutils

LOG = logging.getLogger(__name__)

class Validator:
    """[murraju]Consider moving the validators to a shared
    attributes class
    """

    #UUID Validator
    #Used by type() regex to check if IDs are UUID
    HEX_ELEM = '[0-9A-Fa-f]'
    UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}',
                             HEX_ELEM + '{4}', HEX_ELEM + '{4}',
                             HEX_ELEM + '{12}'])

class HasTenant(object):
    """Tenant mixin, add to subclasses that have a tenant."""
    # NOTE(jkoelker) tenant_id is just a free form string ;(
    tenant_id = sa.Column(sa.String(255))

class HasId(object):
    """id mixin, add to subclasses that have an id."""
    id = sa.Column(sa.String(36), primary_key=True, default=utils.str_uuid)

class IPAvailabilityRange(model_base.BASEV2):
    """Internal representation of available IPs for Quantum subnets.

    Allocation - first entry from the range will be allocated.
    If the first entry is equal to the last entry then this row
    will be deleted.
    Recycling ips involves appending to existing ranges. This is
    only done if the range is contiguous. If not, the first_ip will be
    the same as the last_ip. When adjacent ips are recycled the ranges
    will be merged.

    """
    allocation_pool_id = sa.Column(sa.String(36),
                                   sa.ForeignKey('ipallocationpools.id',
                                                 ondelete="CASCADE"),
                                   nullable=True,
                                   primary_key=True)
    first_ip = sa.Column(sa.String(64), nullable=False, primary_key=True)
    last_ip = sa.Column(sa.String(64), nullable=False, primary_key=True)

    def __repr__(self):
        return "%s - %s" % (self.first_ip, self.last_ip)

class IPAllocationPool(model_base.BASEV2, HasId):
    """Representation of an allocation pool in a Quantum subnet."""

    subnet_id = sa.Column(sa.String(36), sa.ForeignKey('subnets.id',
                                                       ondelete="CASCADE"),
                          nullable=True)
    first_ip = sa.Column(sa.String(64), nullable=False)
    last_ip = sa.Column(sa.String(64), nullable=False)
    available_ranges = orm.relationship(IPAvailabilityRange,
                                        backref='ipallocationpool',
                                        lazy="dynamic")

    def __repr__(self):
        return "%s - %s" % (self.first_ip, self.last_ip)

class IPAllocation(model_base.BASEV2, HasId):
    """Internal representation of allocated IP addresses in a Quantum subnet.
    """
    port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id',
                                                     ondelete="CASCADE"),
                        nullable=True)
    ip_address = sa.Column(sa.String(64), nullable=False, primary_key=True)
    subnet_id = sa.Column(sa.String(36), sa.ForeignKey('subnets.id',
                                                       ondelete="CASCADE"),
                          nullable=False, primary_key=True)
    network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id",
                                                        ondelete="CASCADE"),
                           nullable=False, primary_key=True)
    expiration = sa.Column(sa.DateTime, nullable=True)

class Port(model_base.BASEV2, HasId, HasTenant):
    """Represents a port on a quantum v2 network."""
    name = sa.Column(sa.String(255))
    network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id"),
                           nullable=False)
    fixed_ips = orm.relationship(IPAllocation, backref='ports', lazy="dynamic")
    mac_address = sa.Column(sa.String(32), nullable=False)
    admin_state_up = sa.Column(sa.Boolean(), nullable=False)
    status = sa.Column(sa.String(16), nullable=False)
    device_id = sa.Column(sa.String(255), nullable=False)
    device_owner = sa.Column(sa.String(255), nullable=False)

class DNSNameServer(model_base.BASEV2):
    """Internal representation of a DNS nameserver."""
    address = sa.Column(sa.String(128), nullable=False, primary_key=True)
    subnet_id = sa.Column(sa.String(36),
                          sa.ForeignKey('subnets.id',
                                        ondelete="CASCADE"),
                          primary_key=True)

class Route(model_base.BASEV2):
    """Represents a route for a subnet or port."""
    destination = sa.Column(sa.String(64), nullable=False, primary_key=True)
    nexthop = sa.Column(sa.String(64), nullable=False, primary_key=True)
    subnet_id = sa.Column(sa.String(36),
                          sa.ForeignKey('subnets.id',
                                        ondelete="CASCADE"),
                          primary_key=True)

class Subnet(model_base.BASEV2, HasId, HasTenant):
    """Represents a quantum subnet.

    When a subnet is created the first and last entries will be created. These
    are used for the IP allocation.
    """
    name = sa.Column(sa.String(255))
    network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id'))
    ip_version = sa.Column(sa.Integer, nullable=False)
    cidr = sa.Column(sa.String(64), nullable=False)
    gateway_ip = sa.Column(sa.String(64))
    allocation_pools = orm.relationship(IPAllocationPool,
                                        backref='subnet',
                                        lazy="dynamic")
    enable_dhcp = sa.Column(sa.Boolean())
    dns_nameservers = orm.relationship(DNSNameServer,
                                       backref='subnet',
                                       cascade='delete')
    routes = orm.relationship(Route,
                              backref='subnet',
                              cascade='delete')
    shared = sa.Column(sa.Boolean)

class Network(model_base.BASEV2, HasId, HasTenant):
    """Represents a v2 quantum network."""
    name = sa.Column(sa.String(255))
    ports = orm.relationship(Port, backref='networks')
    subnets = orm.relationship(Subnet, backref='networks')
    status = sa.Column(sa.String(16))
    admin_state_up = sa.Column(sa.Boolean)
    shared = sa.Column(sa.Boolean)

#DreamHost PortFoward, Firewall(FilterRule), AddressBook models as
#Quantum extensions

class PortForward(model_base.BASEV2, HasId, HasTenant):
    """Represents a PortForward extension"""

    name = sa.Column(sa.String(255))
    public_port = sa.Column(sa.Integer, nullable=False)
    instance_id = sa.Column(sa.String(36), nullable=False)
    private_port = sa.Column(sa.Integer, nullable=True)
    # Quantum port address are stored in ipallocation which are internally
    # referred to as fixed_id, thus the name below.
    # XXX can we add a docsting to this model that explains how fixed_id is
    # used?
    fixed_id = sa.Column(
        sa.String(36), sa.ForeignKey('ipallocations.id',
            ondelete="CASCADE"),
        nullable=True)
    op_status = Column(String(16))

    #PortForward Model Validators using sqlalchamey simple validators

    @validates('name')
    def validate_name(self, key, name):
        assert isinstance(name, basestring)
        assert len(name) <= 255
        return name

    @validates('public_port')
    def validate_public_port(self, key, public_port):
        public_port = int(public_port)
        assert public_port >= 0 and public_port <= 65536
        return public_port

    @validates('instance_id')
    def validate_instance_id(self, key, instance_id):
        retype = type(re.compile(Validator.UUID_PATTERN))
        assert isinstance(re.compile(instance_id), retype)
        assert len(instance_id) <= 36
        return instance_id

    @validates('private_port')
    def validate_private_port(self, key, private_port):
        private_port = int(private_port)
        assert private_port >= 0 and private_port <= 65536
        return private_port

    @validates('fixed_id')
    def validate_fixed_id(self, key, fixed_id):
        retype = type(re.compile(Validator.UUID_PATTERN))
        assert isinstance(re.compile(fixed_id), retype)
        assert len(fixed_id) <= 36
        return fixed_id

    @validates('op_status')
    def validate_op_status(self, key, op_status):
        assert isinstance(op_status, basestring)
        assert len(op_status) <= 16
        return op_status

class AddressBookEntry(model_base.BASEV2, HasId, HasTenant):
    """Represents as part of an AddressBook extension"""

    '''[murraju] __tablename__ seems to be needed for plural of models ending
    in 'y' for Quantum DB migrations'''
    __tablename__ = 'addressbookentries'

    group_id = sa.Column(sa.String(36), sa.ForeignKey('addressbookgroups.id'),
        nullable=False)
    cidr = sa.Column(sa.String(64), nullable=False)

    #AddressBookEntry Model Validators using sqlalchamey simple validators
    @validates('group_id')
    def validate_name(self, key, group_id):
        retype = type(re.compile(Validator.UUID_PATTERN))
        assert isinstance(re.compile(group_id), retype)
        assert len(group_id) <= 36
        return group_id

    @validates('cidr')
    def validate_public_port(self, key, cidr):
        assert netaddr.IPNetwork(cidr)
        assert len(cidr) <= 64
        return cidr

class AddressBookGroup(model_base.BASEV2, HasId, HasTenant):
    """Represents as (part of) an AddressBook extension"""

    name = sa.Column(sa.String(255), nullable=False, primary_key=True)
    table_id = sa.Column(sa.String(36), sa.ForeignKey('addressbooks.id'),
        nullable=False)
    entries = orm.relationship(AddressBookEntry, backref='groups')

    #AddressBookGroup Model Validators using sqlalchamey simple validators
    @validates('name')
    def validate_name(self, key, name):
        assert isinstance(name, basestring)
        assert len(name) <= 255
        return name

    @validates('table_id')
    def validate_table_id(self, key, table_id):
        retype = type(re.compile(Validator.UUID_PATTERN))
        assert isinstance(re.compile(table_id), retype)
        assert len(table_id) <= 36
        return table_id

class AddressBook(model_base.BASEV2, HasId, HasTenant):
    """Represents as (part of) an AddressBook extension"""

    name = sa.Column(sa.String(255), nullable=False, primary_key=True)
    groups = orm.relationship(AddressBookGroup, backref='book')

    #AddressBook Model Validators using sqlalchamey simple validators
    @validates('name')
    def validate_name(self, key, name):
        assert isinstance(name, basestring)
        assert len(name) <= 255
        return name

class FilterRule(model_base.BASEV2, HasId, HasTenant):
    """Represents a FilterRule extension"""

    name = sa.Column(sa.String(255))
    action = sa.Column(sa.String(6), nullable=False, primary_key=True)
    ip_version = sa.Column(sa.Integer, nullable=True)
    protocol = sa.Column(sa.String(4), nullable=False)
    source_alias = sa.Column(sa.String(36),
        sa.ForeignKey('addressbookentries.id'),
        nullable=False)
    source_port = sa.Column(sa.Integer, nullable=True)
    destination_alias = sa.Column(sa.String(36),
        sa.ForeignKey('addressbookentries.id'),
        nullable=False)
    destination_port = sa.Column(sa.Integer, nullable=True)
    created_at = sa.Column(sa.DateTime, default=timeutils.utcnow,
         nullable=False)

    #FilterRule Model Validators using sqlalchamey simple validators

    @validates('name')
    def validate_name(self, key, name):
        assert isinstance(name, basestring)
        assert len(name) <= 255
        return name

    @validates('action')
    def validate_action(self, key, action):
        assert isinstance(action, basestring)
        assert len(action) <= 6
        return action

    @validates('ip_version')
    def validate_ip_version(self, key, ip_version):
        assert isinstance(ip_version) is int
        assert isinstance(ip_version, None)
        return ip_version

    @validates('protocol')
    def validate_protocol(self, key, protocol):
        assert isinstance(protocol, basestring)
        assert protocol.lower() in ('tcp', 'udp', 'icmp')
        assert len(protocol) <= 4
        return protocol

    @validates('source_alias')
    def validate_source_alias(self, key, source_alias):
        retype = type(re.compile(Validator.UUID_PATTERN))
        assert isinstance(re.compile(source_alias), retype)
        assert len(source_alias) <= 36
        return source_alias

    @validates('source_port')
    def validate_source_port(self, key, source_port):
        source_port = int(source_port)
        assert source_port >= 0 and source_port <= 65536
        assert len(source_port) <= 36
        return source_port

    @validates('destination_alias')
    def validate_destination_alias(self, key, destination_alias):
        retype = type(re.compile(Validator.UUID_PATTERN))
        assert isinstance(re.compile(destination_alias), retype)
        assert len(destination_alias) <= 36
        return destination_alias

    @validates('destination_port')
    def validate_destination_port(self, key, destination_port):
        destination_port = int(destination_port)
        assert destination_port >= 0 and destination_port <= 65536
        assert len(destination_port) <= 36
        return destination_port

    @validates('created_at')
    def validate_created_at(self, key, created_at):
        assert isinstance(created_at) is datetime
        return created_at
<commit_msg>Removed Validator Class from models_v2.py and using existing attributes.UUID_PATTERN from quantum.api.v2<commit_after># Copyright (c) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# DreamHost Qauntum Extensions
# Copyright 2012 New Dream Network, LLC (DreamHost)
# @author: Murali Raju, New Dream Network, LLC (DreamHost)
# @author: Mark Mcclain, New Dream Network, LLC (DreamHost)

from datetime import datetime
import logging
import netaddr
import re

import sqlalchemy as sa
from sqlalchemy import Column, String
from sqlalchemy import orm
from sqlalchemy.orm import validates

from quantum.api.v2 import attributes
from quantum.api import api_common as common
from quantum.common import utils
from quantum.db import model_base
from quantum.openstack.common import timeutils

LOG = logging.getLogger(__name__)

class HasTenant(object):
    """Tenant mixin, add to subclasses that have a tenant."""
    # NOTE(jkoelker) tenant_id is just a free form string ;(
    tenant_id = sa.Column(sa.String(255))

class HasId(object):
    """id mixin, add to subclasses that have an id."""
    id = sa.Column(sa.String(36), primary_key=True, default=utils.str_uuid)

class IPAvailabilityRange(model_base.BASEV2):
    """Internal representation of available IPs for Quantum subnets.

    Allocation - first entry from the range will be allocated.
    If the first entry is equal to the last entry then this row
    will be deleted.
    Recycling ips involves appending to existing ranges. This is
    only done if the range is contiguous. If not, the first_ip will be
    the same as the last_ip. When adjacent ips are recycled the ranges
    will be merged.

    """
    allocation_pool_id = sa.Column(sa.String(36),
                                   sa.ForeignKey('ipallocationpools.id',
                                                 ondelete="CASCADE"),
                                   nullable=True,
                                   primary_key=True)
    first_ip = sa.Column(sa.String(64), nullable=False, primary_key=True)
    last_ip = sa.Column(sa.String(64), nullable=False, primary_key=True)

    def __repr__(self):
        return "%s - %s" % (self.first_ip, self.last_ip)

class IPAllocationPool(model_base.BASEV2, HasId):
    """Representation of an allocation pool in a Quantum subnet."""

    subnet_id = sa.Column(sa.String(36), sa.ForeignKey('subnets.id',
                                                       ondelete="CASCADE"),
                          nullable=True)
    first_ip = sa.Column(sa.String(64), nullable=False)
    last_ip = sa.Column(sa.String(64), nullable=False)
    available_ranges = orm.relationship(IPAvailabilityRange,
                                        backref='ipallocationpool',
                                        lazy="dynamic")

    def __repr__(self):
        return "%s - %s" % (self.first_ip, self.last_ip)

class IPAllocation(model_base.BASEV2, HasId):
    """Internal representation of allocated IP addresses in a Quantum subnet.
    """
    port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id',
                                                     ondelete="CASCADE"),
                        nullable=True)
    ip_address = sa.Column(sa.String(64), nullable=False, primary_key=True)
    subnet_id = sa.Column(sa.String(36), sa.ForeignKey('subnets.id',
                                                       ondelete="CASCADE"),
                          nullable=False, primary_key=True)
    network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id",
                                                        ondelete="CASCADE"),
                           nullable=False, primary_key=True)
    expiration = sa.Column(sa.DateTime, nullable=True)

class Port(model_base.BASEV2, HasId, HasTenant):
    """Represents a port on a quantum v2 network."""
    name = sa.Column(sa.String(255))
    network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id"),
                           nullable=False)
    fixed_ips = orm.relationship(IPAllocation, backref='ports', lazy="dynamic")
    mac_address = sa.Column(sa.String(32), nullable=False)
    admin_state_up = sa.Column(sa.Boolean(), nullable=False)
    status = sa.Column(sa.String(16), nullable=False)
    device_id = sa.Column(sa.String(255), nullable=False)
    device_owner = sa.Column(sa.String(255), nullable=False)

class DNSNameServer(model_base.BASEV2):
    """Internal representation of a DNS nameserver."""
    address = sa.Column(sa.String(128), nullable=False, primary_key=True)
    subnet_id = sa.Column(sa.String(36),
                          sa.ForeignKey('subnets.id',
                                        ondelete="CASCADE"),
                          primary_key=True)

class Route(model_base.BASEV2):
    """Represents a route for a subnet or port."""
    destination = sa.Column(sa.String(64), nullable=False, primary_key=True)
    nexthop = sa.Column(sa.String(64), nullable=False, primary_key=True)
    subnet_id = sa.Column(sa.String(36),
                          sa.ForeignKey('subnets.id',
                                        ondelete="CASCADE"),
                          primary_key=True)

class Subnet(model_base.BASEV2, HasId, HasTenant):
    """Represents a quantum subnet.

    When a subnet is created the first and last entries will be created. These
    are used for the IP allocation.
    """
    name = sa.Column(sa.String(255))
    network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id'))
    ip_version = sa.Column(sa.Integer, nullable=False)
    cidr = sa.Column(sa.String(64), nullable=False)
    gateway_ip = sa.Column(sa.String(64))
    allocation_pools = orm.relationship(IPAllocationPool,
                                        backref='subnet',
                                        lazy="dynamic")
    enable_dhcp = sa.Column(sa.Boolean())
    dns_nameservers = orm.relationship(DNSNameServer,
                                       backref='subnet',
                                       cascade='delete')
    routes = orm.relationship(Route,
                              backref='subnet',
                              cascade='delete')
    shared = sa.Column(sa.Boolean)

class Network(model_base.BASEV2, HasId, HasTenant):
    """Represents a v2 quantum network."""
    name = sa.Column(sa.String(255))
    ports = orm.relationship(Port, backref='networks')
    subnets = orm.relationship(Subnet, backref='networks')
    status = sa.Column(sa.String(16))
    admin_state_up = sa.Column(sa.Boolean)
    shared = sa.Column(sa.Boolean)

#DreamHost PortFoward, Firewall(FilterRule), AddressBook models as
#Quantum extensions

class PortForward(model_base.BASEV2, HasId, HasTenant):
    """Represents a PortForward extension"""

    name = sa.Column(sa.String(255))
    public_port = sa.Column(sa.Integer, nullable=False)
    instance_id = sa.Column(sa.String(36), nullable=False)
    private_port = sa.Column(sa.Integer, nullable=True)
    # Quantum port address are stored in ipallocation which are internally
    # referred to as fixed_id, thus the name below.
    # XXX can we add a docsting to this model that explains how fixed_id is
    # used?
    fixed_id = sa.Column(
        sa.String(36), sa.ForeignKey('ipallocations.id',
            ondelete="CASCADE"),
        nullable=True)
    op_status = Column(String(16))

    #PortForward Model Validators using sqlalchamey simple validators

    @validates('name')
    def validate_name(self, key, name):
        assert isinstance(name, basestring)
        assert len(name) <= 255
        return name

    @validates('public_port')
    def validate_public_port(self, key, public_port):
        public_port = int(public_port)
        assert public_port >= 0 and public_port <= 65536
        return public_port

    @validates('instance_id')
    def validate_instance_id(self, key, instance_id):
        retype = type(re.compile(attributes.UUID_PATTERN))
        assert isinstance(re.compile(instance_id), retype)
        assert len(instance_id) <= 36
        return instance_id

    @validates('private_port')
    def validate_private_port(self, key, private_port):
        private_port = int(private_port)
        assert private_port >= 0 and private_port <= 65536
        return private_port

    @validates('fixed_id')
    def validate_fixed_id(self, key, fixed_id):
        retype = type(re.compile(attributes.UUID_PATTERN))
        assert isinstance(re.compile(fixed_id), retype)
        assert len(fixed_id) <= 36
        return fixed_id

    @validates('op_status')
    def validate_op_status(self, key, op_status):
        assert isinstance(op_status, basestring)
        assert len(op_status) <= 16
        return op_status

class AddressBookEntry(model_base.BASEV2, HasId, HasTenant):
    """Represents as part of an AddressBook extension"""

    '''[murraju] __tablename__ seems to be needed for plural of models ending
    in 'y' for Quantum DB migrations'''
    __tablename__ = 'addressbookentries'

    group_id = sa.Column(sa.String(36), sa.ForeignKey('addressbookgroups.id'),
        nullable=False)
    cidr = sa.Column(sa.String(64), nullable=False)

    #AddressBookEntry Model Validators using sqlalchamey simple validators
    @validates('group_id')
    def validate_name(self, key, group_id):
        retype = type(re.compile(attributes.UUID_PATTERN))
        assert isinstance(re.compile(group_id), retype)
        assert len(group_id) <= 36
        return group_id

    @validates('cidr')
    def validate_public_port(self, key, cidr):
        assert netaddr.IPNetwork(cidr)
        assert len(cidr) <= 64
        return cidr

class AddressBookGroup(model_base.BASEV2, HasId, HasTenant):
    """Represents as (part of) an AddressBook extension"""

    name = sa.Column(sa.String(255), nullable=False, primary_key=True)
    table_id = sa.Column(sa.String(36), sa.ForeignKey('addressbooks.id'),
        nullable=False)
    entries = orm.relationship(AddressBookEntry, backref='groups')

    #AddressBookGroup Model Validators using sqlalchamey simple validators
    @validates('name')
    def validate_name(self, key, name):
        assert isinstance(name, basestring)
        assert len(name) <= 255
        return name

    @validates('table_id')
    def validate_table_id(self, key, table_id):
        retype = type(re.compile(attributes.UUID_PATTERN))
        assert isinstance(re.compile(table_id), retype)
        assert len(table_id) <= 36
        return table_id

class AddressBook(model_base.BASEV2, HasId, HasTenant):
    """Represents as (part of) an AddressBook extension"""

    name = sa.Column(sa.String(255), nullable=False, primary_key=True)
    groups = orm.relationship(AddressBookGroup, backref='book')

    #AddressBook Model Validators using sqlalchamey simple validators
    @validates('name')
    def validate_name(self, key, name):
        assert isinstance(name, basestring)
        assert len(name) <= 255
        return name

class FilterRule(model_base.BASEV2, HasId, HasTenant):
    """Represents a FilterRule extension"""

    name = sa.Column(sa.String(255))
    action = sa.Column(sa.String(6), nullable=False, primary_key=True)
    ip_version = sa.Column(sa.Integer, nullable=True)
    protocol = sa.Column(sa.String(4), nullable=False)
    source_alias = sa.Column(sa.String(36),
        sa.ForeignKey('addressbookentries.id'),
        nullable=False)
    source_port = sa.Column(sa.Integer, nullable=True)
    destination_alias = sa.Column(sa.String(36),
        sa.ForeignKey('addressbookentries.id'),
        nullable=False)
    destination_port = sa.Column(sa.Integer, nullable=True)
    created_at = sa.Column(sa.DateTime, default=timeutils.utcnow,
         nullable=False)

    #FilterRule Model Validators using sqlalchamey simple validators

    @validates('name')
    def validate_name(self, key, name):
        assert isinstance(name, basestring)
        assert len(name) <= 255
        return name

    @validates('action')
    def validate_action(self, key, action):
        assert isinstance(action, basestring)
        assert len(action) <= 6
        return action

    @validates('ip_version')
    def validate_ip_version(self, key, ip_version):
        assert isinstance(ip_version) is int
        assert isinstance(ip_version, None)
        return ip_version

    @validates('protocol')
    def validate_protocol(self, key, protocol):
        assert isinstance(protocol, basestring)
        assert protocol.lower() in ('tcp', 'udp', 'icmp')
        assert len(protocol) <= 4
        return protocol

    @validates('source_alias')
    def validate_source_alias(self, key, source_alias):
        retype = type(re.compile(attributes.UUID_PATTERN))
        assert isinstance(re.compile(source_alias), retype)
        assert len(source_alias) <= 36
        return source_alias

    @validates('source_port')
    def validate_source_port(self, key, source_port):
        source_port = int(source_port)
        assert source_port >= 0 and source_port <= 65536
        assert len(source_port) <= 36
        return source_port

    @validates('destination_alias')
    def validate_destination_alias(self, key, destination_alias):
        retype = type(re.compile(attributes.UUID_PATTERN))
        assert isinstance(re.compile(destination_alias), retype)
        assert len(destination_alias) <= 36
        return destination_alias

    @validates('destination_port')
    def validate_destination_port(self, key, destination_port):
        destination_port = int(destination_port)
        assert destination_port >= 0 and destination_port <= 65536
        assert len(destination_port) <= 36
        return destination_port

    @validates('created_at')
    def validate_created_at(self, key, created_at):
        assert isinstance(created_at) is datetime
        return created_at
<|endoftext|>


<commit_before>#!/usr/bin/env python

# Copyright 2017, Ross A. Beyer (rbeyer@seti.org)
# The functions orientation(), hulls(), rotatingCalipers(), and diameter()
#    are Copyright 2002, David Eppstein, under a Python Software License.
# The function haversine() is derived from algorithms which
#   are Copyright 2002-2017, Chris Veness, under an MIT license
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# The copyright status of the orientation, hulls, rotatingCalipers, and diameter
# functions by David Eppstein are uncertain.  The margin of the webpage referenced
# indicates that the code may be under a Python Software License, and this work assumes
# that is their copyright status.  The Python Software License is compatible with the
# Apache 2 License, and can be found at
# https://opensource.org/licenses/PythonSoftFoundation.php

# This program is for reading a Shapefile, exracting feature information,
# and computing centroid and bounding box and some other characteristics for each feature.

import os, sys, optparse, ConfigParser, math
from osgeo import ogr, osr
from geopy import distance

def man(option, opt, value, parser):
    print >>sys.stderr, parser.usage
    print >>sys.stderr, '''\
This program extracts shapes and their boundaries from a Shapefile.
'''
    sys.exit()

class Usage(Exception):
    def __init__(self, msg):
        self.msg = msg

# The next four functions (orientation, hulls, rotatingCalipers, and diameter) are
# from David Eppstein at https://code.activestate.com/recipes/117225/ with a minor change
# to diameter to also return the 'diam' parameter (which is the diameter squared).
#
# convex hull (Graham scan by x-coordinate) and diameter of a set of points
# David Eppstein, UC Irvine, 7 Mar 2002

#from __future__ import generators

def orientation(p,q,r):
    '''Return positive if p-q-r are clockwise, neg if ccw, zero if colinear.'''
    return (q[1]-p[1])*(r[0]-p[0]) - (q[0]-p[0])*(r[1]-p[1])

def hulls(Points):
    '''Graham scan to find upper and lower convex hulls of a set of 2d points.'''
    U = []
    L = []
    Points.sort()
    for p in Points:
        while len(U) > 1 and orientation(U[-2],U[-1],p) <= 0: U.pop()
        while len(L) > 1 and orientation(L[-2],L[-1],p) >= 0: L.pop()
        U.append(p)
        L.append(p)
    return U,L

def rotatingCalipers(Points):
    '''Given a list of 2d points, finds all ways of sandwiching the points
between two parallel lines that touch one point each, and yields the sequence
of pairs of points touched by each pair of lines.'''
    U,L = hulls(Points)
    i = 0
    j = len(L) - 1
    while i < len(U) - 1 or j > 0:
        yield U[i],L[j]

        # if all the way through one side of hull, advance the other side
        if i == len(U) - 1: j -= 1
        elif j == 0: i += 1

        # still points left on both lists, compare slopes of next hull edges
        # being careful to avoid divide-by-zero in slope calculation
        elif (U[i+1][1]-U[i][1])*(L[j][0]-L[j-1][0]) > \
                (L[j][1]-L[j-1][1])*(U[i+1][0]-U[i][0]):
            i += 1
        else: j -= 1

def diameter(Points):
    '''Given a list of 2d points, returns the pair that's farthest apart.'''
    diam,pair = max([((p[0]-q[0])**2 + (p[1]-q[1])**2, (p,q))
                     for p,q in rotatingCalipers(Points)])
    return diam, pair

def haversine( lon1, lat1, lon2, lat2, radius):
    # The material from which this haversine() function was created is
    # Copyright 2002-2017 by Chris Veness under an MIT license, and is
    # available on his website at: http://www.movable-type.co.uk/scripts/latlong.html
    phi1 = math.radians( float(lat1) )
    phi2 = math.radians( float(lat2) )
    dlat = math.radians( float(lat2) - float(lat1) )
    dlon = math.radians( float(lon2) - float(lon1) )

    a = math.pow( math.sin(dlat/2),2 ) + ( math.cos( phi1 ) * math.cos( phi2 ) * math.pow( math.sin(dlon/2), 2 ) )
    c = 2 * math.atan2( math.sqrt(a), math.sqrt(1-a) )
    distance = radius * c
    return distance

def format_coord( coord, decimals, lon360 ):
    lon = coord[0]
    lat = coord[1]

    format_str =  "{:."+str(decimals)+"f}"

    if( lon360 and lon < 0): lon += 360
    #outline = format_str.format(lon) +' E, '+ format_str.format(lat)
    formatted = (format_str.format(lon) +' E', format_str.format(lat))
    return formatted

def main():
    try:
        try:
            usage = "usage: shp2bbox.py [--help][--manual][--format <n>][--lon360] <Shapefile(s)>"
            parser = optparse.OptionParser(usage=usage)
            parser.set_defaults(decimals=2)
            parser.set_defaults(lon360=0)
            parser.add_option("--manual", action="callback", callback=man,
                              help="Read the manual.")
            parser.add_option("--format", "-f", dest="decimals",
                              help="Number of digits past the decimal, default is 2.")
            parser.add_option("--lon360", "-l", dest="lon360", action="store_true",
                              help="Change longitudes to 0 to 360 range, default is -180 to 180.")
            parser.add_option("--proposal", "-p", dest="listing", action="store_true",
                              help="List parameters out atomically.")

            (options, args) = parser.parse_args()

            if not args: parser.error("need Shapefile file")

            for filename in args:
                if not os.path.exists( filename ): parser.error( filename+" is not a file.")

        except optparse.OptionError, msg:
            raise Usage(msg)

        for shp in args:
            driver = ogr.GetDriverByName("ESRI Shapefile")
            try:
                dataSource = driver.Open(shp, 0)
            except optparse.OptionError, msg:
                raise Usage(msg)
            layer = dataSource.GetLayer()

            for feature in layer:
                try:
                    name = feature.GetField("Name")
                    desc = feature.GetField("Descriptor")
                except ValueError:
                    name = 'Feature'
                    for i in range(layer.GetLayerDefn().GetFieldCount()):
                        name += ' '+str( feature.GetField(i) )
                    desc = ''
                geom = feature.GetGeometryRef()
                spatialRef = geom.GetSpatialReference()

                longlat_srs = osr.SpatialReference()
                longlat_srs.ImportFromProj4('+proj=longlat')
                ct = osr.CoordinateTransformation(spatialRef, longlat_srs)

                # This may be problematic for concave shapes.  Trent recommends using a geodesic centroid,
                # for now, we'll just do it like this.  Trent says:
                # See Jenness: http://www.jennessent.com/downloads/Graphics_Shapes_Online.pdf
                # although this post has some concerns on how Jenness does it:
                # https://gis.stackexchange.com/questions/43505/calculating-a-spherical-polygon-centroid

                centroid_lon_lat = ct.TransformPoint( geom.Centroid().GetX(), geom.Centroid().GetY() )

                envelope = geom.GetEnvelope()
                bbox_lon_lat_min = ct.TransformPoint( envelope[0], envelope[2] )
                bbox_lon_lat_max = ct.TransformPoint( envelope[1], envelope[3] )

                print name+' '+desc+': '
                #print geom.Centroid()
                centroid = format_coord( centroid_lon_lat, options.decimals, options.lon360 )
                min_point = format_coord( bbox_lon_lat_min, options.decimals, options.lon360 )
                max_point = format_coord( bbox_lon_lat_max, options.decimals, options.lon360 )
                if options.listing:
                    print 'Center latitude: '+centroid[1]
                    print 'Center longitude: '+centroid[0]
                    print 'Northernmost latitude: '+ max_point[1]
                    print 'Southernmost latitude: '+ min_point[1]
                    print 'Westernmost longitude: '+ min_point[0]
                    print 'Easternmost longitude: '+ max_point[0]
                else:
                    print '        Centroid: '+centroid[0]+', '+centroid[1]
                    #print envelope
                    print '    Bounding box: '+min_point[0]+', '+min_point[1]+' and '+max_point[0]+', '+max_point[1]

                format_str =  "{:."+str(options.decimals)+"f}"

                # We are going to transform the geometry to a projection centered at the
                # centroid, so that we can more accurately compute the area and longest dimension:
                xform_srs = osr.SpatialReference()
                xform_srs.ImportFromProj4('+proj=ortho +lat_0='+str(centroid_lon_lat[1])+' +lon_0='+str(centroid_lon_lat[0])+' +a='+str(spatialRef.GetSemiMajor())+' +b='+str(spatialRef.GetSemiMinor()))
                #xform_srs.ImportFromProj4('+proj=sinu +lat_0='+str(centroid_lon_lat[1])+' +lon_0='+str(centroid_lon_lat[0])+' +a='+str(spatialRef.GetSemiMajor())+' +b='+str(spatialRef.GetSemiMinor()))
                xform = osr.CoordinateTransformation(spatialRef, xform_srs)

                geom.Transform(xform)

                if options.listing:
                    print 'Area: '+format_str.format( geom.GetArea()/1000000 )+' km2'
                else:
                    print '            Area: '+format_str.format( geom.GetArea()/1000000 )+' km^2'

                ring = geom.GetGeometryRef(0)
                pairs = []
                for p in range( ring.GetPointCount() ):
                    x, y, z = ring.GetPoint(p)
                    pairs.append( [x, y] )

                diam, pair = diameter( pairs )
                print 'Orthographic dist: '+format_str.format( (math.sqrt(diam))/1000 )+' km'

                xtoll = osr.CoordinateTransformation(xform_srs, longlat_srs)

                #geom.Transform( xtoll )
                #geom.Transform( ct )
                #llring = geom.GetGeometryRef(0)
                #llpairs = []
                #for p in range( llring.GetPointCount() ):
                #    x, y, z = llring.GetPoint(p)
                #    llpairs.append( [x, y] )

                #lldiam, llpair = diameter( llpairs )

                llpoint1 =  xtoll.TransformPoint( pair[0][0], pair[0][1] )
                llpoint2 =  xtoll.TransformPoint( pair[1][0], pair[1][1] )

                haverdist = haversine( llpoint1[0], llpoint1[1], llpoint2[0], llpoint2[1], spatialRef.GetSemiMajor() )
                print 'Haversine dist: '+format_str.format( haverdist/1000 )+' km'

                print 'Vincenty dist: '+str(distance.vincenty( llpoint1, llpoint2, ellipsoid=(spatialRef.GetSemiMajor()/1000, spatialRef.GetSemiMinor()/1000,spatialRef.GetInvFlattening()) ) )
                #print 'Vincenty dist: '+format_str.format( distance.vincenty( llpair[0], llpair[1], ellipsoid=(spatialRef.GetSemiMajor(), spatialRef.GetSemiMinor(),spatialRef.GetInvFlattening()) )/1000 )+' km'
                print llpoint1
                print llpoint2

                ## # Brute force to find the longest span:
                ## geom.Transform(ct)

                ## ring = geom.GetGeometryRef(0)
                ## pairs = []
                ## for p in range( ring.GetPointCount() ):
                ##     x, y, z = ring.GetPoint(p)
                ##     pairs.append( [x, y] )

                ## max_vincenty = 0
                ## pair_vincenty = []
                ## max_haver = 0
                ## pair_vincenty = []
                ## for p in pairs:
                ##     for q in pairs:
                ##         l_vincenty = distance.vincenty( p, q, ellipsoid=(spatialRef.GetSemiMajor()/1000, spatialRef.GetSemiMinor()/1000,spatialRef.GetInvFlattening()) ).km
                ##         if l_vincenty > max_vincenty:
                ##             max_vincenty = l_vincenty
                ##             pair_vincenty = (p,q)
                ##         l_haver = haversine( p[0], p[1], q[0], q[1], spatialRef.GetSemiMajor() )/1000
                ##         if l_haver> max_haver:
                ##             max_haver= l_haver
                ##             pair_haver = (p,q)
                ## print 'Max Haversine: '+str(max_haver)
                ## print pair_haver
                ## print 'Max Vincenty: '+str(max_vincenty)
                ## print pair_vincenty

    except Usage, err:
        print >>sys.stderr, err.msg
        # print >>sys.stderr, "for help use --help"
        return 2

    # # To more easily debug this program, comment out this catch block.
    # except Exception, err:
    #     sys.stderr.write( str(err) + '\n' )
    #     return 1

if __name__ == "__main__":
    sys.exit(main())
<commit_msg>Needed to update for Python 3, also geopy updated.<commit_after>#!/usr/bin/env python
'''Reads a Shapefile, exracting feature information. Computes centroid, bounding box, and some other characteristics for each feature.'''

# Copyright 2017, 2019, Ross A. Beyer (rbeyer@seti.org)
# The functions orientation(), hulls(), rotatingCalipers(), and diameter()
#    are Copyright 2002, David Eppstein, under a Python Software License.
# The function haversine() is derived from algorithms which
#   are Copyright 2002-2017, Chris Veness, under an MIT license
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# The copyright status of the orientation, hulls, rotatingCalipers, and diameter
# functions by David Eppstein are uncertain.  The margin of the webpage referenced
# indicates that the code may be under a Python Software License, and this work assumes
# that is their copyright status.  The Python Software License is compatible with the
# Apache 2 License, and can be found at
# https://opensource.org/licenses/PythonSoftFoundation.php

# This program is for reading a Shapefile, exracting feature information,
# and computing centroid and bounding box and some other characteristics for each feature.

import argparse
import math
import os
import sys
from osgeo import ogr, osr
from geopy import distance

# The next four functions (orientation, hulls, rotatingCalipers, and diameter) are
# from David Eppstein at https://code.activestate.com/recipes/117225/ with a minor change
# to diameter to also return the 'diam' parameter (which is the diameter squared).
#
# convex hull (Graham scan by x-coordinate) and diameter of a set of points
# David Eppstein, UC Irvine, 7 Mar 2002

# from __future__ import generators

def orientation(p, q, r):
    '''Return positive if p-q-r are clockwise, neg if ccw, zero if colinear.'''
    return (q[1] - p[1]) * (r[0] - p[0]) - (q[0] - p[0]) * (r[1] - p[1])

def hulls(Points):
    '''Graham scan to find upper and lower convex hulls of a set of 2d points.'''
    U = []
    L = []
    Points.sort()
    for p in Points:
        while len(U) > 1 and orientation(U[-2], U[-1], p) <= 0:
            U.pop()
        while len(L) > 1 and orientation(L[-2], L[-1], p) >= 0:
            L.pop()
        U.append(p)
        L.append(p)
    return U, L

def rotatingCalipers(Points):
    '''Given a list of 2d points, finds all ways of sandwiching the points
between two parallel lines that touch one point each, and yields the sequence
of pairs of points touched by each pair of lines.'''
    U, L = hulls(Points)
    i = 0
    j = len(L) - 1
    while i < len(U) - 1 or j > 0:
        yield U[i], L[j]

        # if all the way through one side of hull, advance the other side
        if i == len(U) - 1:
            j -= 1
        elif j == 0:
            i += 1

        # still points left on both lists, compare slopes of next hull edges
        # being careful to avoid divide-by-zero in slope calculation
        elif (U[i + 1][1] - U[i][1]) * (L[j][0] - L[j - 1][0]) > \
                (L[j][1] - L[j - 1][1]) * (U[i + 1][0] - U[i][0]):
            i += 1
        else:
            j -= 1

def diameter(Points):
    '''Given a list of 2d points, returns the pair that's farthest apart.'''
    diam, pair = max([((p[0] - q[0])**2 + (p[1] - q[1])**2, (p, q)) for p, q in rotatingCalipers(Points)])
    return diam, pair

def haversine(lon1, lat1, lon2, lat2, radius):
    # The material from which this haversine() function was created is
    # Copyright 2002-2017 by Chris Veness under an MIT license, and is
    # available on his website at: http://www.movable-type.co.uk/scripts/latlong.html
    phi1 = math.radians(float(lat1))
    phi2 = math.radians(float(lat2))
    dlat = math.radians(float(lat2) - float(lat1))
    dlon = math.radians(float(lon2) - float(lon1))

    a = math.pow(math.sin(dlat / 2), 2) + (math.cos(phi1) * math.cos(phi2) * math.pow(math.sin(dlon / 2), 2))
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
    distance = radius * c
    return distance

def format_coord(coord, decimals, lon360):
    lon = coord[0]
    lat = coord[1]

    format_str = "{:." + str(decimals) + "f}"

    if(lon360 and lon < 0):
        lon += 360
    # outline = format_str.format(lon) +' E, '+ format_str.format(lat)
    formatted = (format_str.format(lon) + ' E', format_str.format(lat))
    return formatted

def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('-d', '--decimals', default=2,
                        help="Number of digits past the decimal, default is 2.")
    parser.add_argument('-l', '--lon360', action="store_true",
                        help="Change longitudes to 0 to 360 range, default is -180 to 180.")
    parser.add_argument('-p', '--parameters', action="store_true",
                        help="List parameters out atomically.")
    parser.add_argument('shpfile', help="shape files",
                        nargs='+')

    args = parser.parse_args()

    # for filename in args:
    # if not os.path.exists( filename ): parser.error( filename+" is not a file.")

    for shp in args.shpfile:
        driver = ogr.GetDriverByName("ESRI Shapefile")
        dataSource = driver.Open(shp, 0)
        layer = dataSource.GetLayer()

        for feature in layer:
            try:
                name = feature.GetField("Name")
                desc = feature.GetField("Descriptor")
            except ValueError:
                name = 'Feature'
                for i in range(layer.GetLayerDefn().GetFieldCount()):
                    name += ' ' + str(feature.GetField(i))
                desc = ''
            geom = feature.GetGeometryRef()
            spatialRef = geom.GetSpatialReference()

            longlat_srs = osr.SpatialReference()
            longlat_srs.ImportFromProj4('+proj=longlat +a={} +b={}'.format(spatialRef.GetSemiMajor(),
                                                                           spatialRef.GetSemiMinor()))
            ct = osr.CoordinateTransformation(spatialRef, longlat_srs)

            # This may be problematic for concave shapes.
            # Trent recommends using a geodesic centroid,
            # for now, we'll just do it like this.  Trent says:
            # See Jenness: http://www.jennessent.com/downloads/Graphics_Shapes_Online.pdf
            # although this post has some concerns on how Jenness does it:
            # https://gis.stackexchange.com/questions/43505/calculating-a-spherical-polygon-centroid
            centroid_lon_lat = ct.TransformPoint(geom.Centroid().GetX(),
                                                 geom.Centroid().GetY())

            envelope = geom.GetEnvelope()
            bbox_lon_lat_min = ct.TransformPoint(envelope[0], envelope[2])
            bbox_lon_lat_max = ct.TransformPoint(envelope[1], envelope[3])

            print(f'{name} {desc}:')
            # print geom.Centroid()
            centroid = format_coord(centroid_lon_lat, args.decimals, args.lon360)
            min_point = format_coord(bbox_lon_lat_min, args.decimals, args.lon360)
            max_point = format_coord(bbox_lon_lat_max, args.decimals, args.lon360)
            if args.parameters:
                print(f'Center latitude: {centroid[1]}')
                print(f'Center longitude: {centroid[0]}')
                print(f'Northernmost latitude: {max_point[1]}')
                print(f'Southernmost latitude: {min_point[1]}')
                print(f'Westernmost longitude: {min_point[0]}')
                print(f'Easternmost longitude: {max_point[0]}')
            else:
                print(f'        Centroid: {centroid[0]}, {centroid[1]}')
                # print envelope
                print('    Bounding box: {}, {} and {}, {}'.format(min_point[0],
                                                                   min_point[1],
                                                                   max_point[0],
                                                                   max_point[1]))

            format_str = "{:." + str(args.decimals) + "f}"

            # We are going to transform the geometry to a projection centered at the
            # centroid, so that we can more accurately compute the area and longest dimension:
            xform_srs = osr.SpatialReference()
            xform_srs.ImportFromProj4('+proj=ortho +lat_0={} +lon_0={} +a={} +b={}'.format(centroid_lon_lat[1],
                                                                                centroid_lon_lat[0],
                                                                                spatialRef.GetSemiMajor(),
                                                                                spatialRef.GetSemiMinor()))
            # xform_srs.ImportFromProj4('+proj=sinu +lat_0='+str(centroid_lon_lat[1])+' +lon_0='+str(centroid_lon_lat[0])+' +a='+str(spatialRef.GetSemiMajor())+' +b='+str(spatialRef.GetSemiMinor()))
            xform = osr.CoordinateTransformation(spatialRef, xform_srs)

            geom.Transform(xform)

            area_str = 'Area: ' + format_str.format(geom.GetArea() / 1000000) + ' km^2'
            if args.parameters:
                print(area_str)
            else:
                print('            ' + area_str)

            ring = geom.GetGeometryRef(0)
            pairs = []
            for p in range(ring.GetPointCount()):
                x, y, z = ring.GetPoint(p)
                pairs.append([x, y])

            diam, pair = diameter(pairs)
            print('Orthographic dist: ' + format_str.format((math.sqrt(diam)) /
                                                            1000) + ' km')

            xtoll = osr.CoordinateTransformation(xform_srs, longlat_srs)

            # geom.Transform( xtoll )
            # geom.Transform( ct )
            # llring = geom.GetGeometryRef(0)
            # llpairs = []
            # for p in range( llring.GetPointCount() ):
            #     x, y, z = llring.GetPoint(p)
            #     llpairs.append( [x, y] )

            # lldiam, llpair = diameter( llpairs )

            llpoint1 = xtoll.TransformPoint(pair[0][0], pair[0][1])
            llpoint2 = xtoll.TransformPoint(pair[1][0], pair[1][1])

            # print(f'llpoint1: {llpoint1}')
            # print(f'llpoint2: {llpoint2}')

            haverdist = haversine(llpoint1[0], llpoint1[1], llpoint2[0], llpoint2[1], spatialRef.GetSemiMajor())
            print('Haversine dist: ' + format_str.format(haverdist / 1000) + ' km')

            # print('Vincenty dist: ' + str(distance.vincenty((llpoint1[1], llpoint1[0]),
            print('Geodesic dist: ' + str(distance.geodesic((llpoint1[1], llpoint1[0]),
                                                            (llpoint2[1], llpoint2[0]),
                                                            ellipsoid=(spatialRef.GetSemiMajor() / 1000,
                                                                       spatialRef.GetSemiMinor() / 1000,
                                                                       spatialRef.GetInvFlattening()))))
            # print 'Vincenty dist: '+format_str.format( distance.vincenty( llpair[0], llpair[1], ellipsoid=(spatialRef.GetSemiMajor(), spatialRef.GetSemiMinor(),spatialRef.GetInvFlattening()) )/1000 )+' km'
            # print(llpoint1)
            # print(llpoint2)

            # # Brute force to find the longest span:
            # geom.Transform(ct)

            # ring = geom.GetGeometryRef(0)
            # pairs = []
            # for p in range( ring.GetPointCount() ):
            #     x, y, z = ring.GetPoint(p)
            #     pairs.append( [x, y] )

            # max_vincenty = 0
            # pair_vincenty = []
            # max_haver = 0
            # pair_vincenty = []
            # for p in pairs:
            #     for q in pairs:
            #         l_vincenty = distance.vincenty( p, q, ellipsoid=(spatialRef.GetSemiMajor()/1000, spatialRef.GetSemiMinor()/1000,spatialRef.GetInvFlattening()) ).km
            #         if l_vincenty > max_vincenty:
            #             max_vincenty = l_vincenty
            #             pair_vincenty = (p,q)
            #         l_haver = haversine( p[0], p[1], q[0], q[1], spatialRef.GetSemiMajor() )/1000
            #         if l_haver> max_haver:
            #             max_haver= l_haver
            #             pair_haver = (p,q)
            # print 'Max Haversine: '+str(max_haver)
            # print pair_haver
            # print 'Max Vincenty: '+str(max_vincenty)
            # print pair_vincenty

if __name__ == "__main__":
    sys.exit(main())
<|endoftext|>